﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Collections;
using System.Data.SqlTypes;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.DataFace;
using Noris.Schedule.Support.Services;

using Noris.Schedule.Planning.DataFace;
using System.Drawing;
using Noris.Schedule.Planning.ProcessData;

namespace Noris.Schedule.Planning.ProcessData
{
	#region CLASS AxisHeapCls : v sobě fyzicky uchovává data materiálové osy S a kapacitní úkoly, a vztahy mezi nimi.
	/// <summary>
	/// Třída AxisHeapCls v sobě fyzicky uchovává data materiálové osy S a kapacitní úkoly, a vztahy mezi nimi.
	/// </summary>
	public partial class AxisHeapCls : PlanningComponentCls, IDisposable
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor s předáním parametrů
		/// </summary>
		/// <param name="parent"></param>
		internal AxisHeapCls(PlanningComponentCls parent)
			: base(parent)
		{
			// base konstruktor uloží předaný objekt a vyvolá CreateData()
		}
		/// <summary>
		/// Reference na objekt Parent = základna plánovacího procesu
		/// </summary>
		internal PlanningDataCls ParentPlanning { get { return this.GetParent<PlanningDataCls>(); } }
        /// <summary>
        /// Auditor: obsahuje informace a chyby aktuální verze plánu
        /// </summary>
        internal AuditorCls Auditor { get { return this.ParentPlanning.Auditor; } }
        /// <summary>
		/// Vytvoří interní prázdné struktury, nenačítá data
		/// </summary>
		protected override void DataCreate()
		{
			_AxisS = new Dictionary<int, MaterialPlanAxisItemCls>();
			_TaskC = new Dictionary<int, CapacityPlanWorkItemCls>();
			_WorkD = new Dictionary<int, WorkUnitCls>();
			_RelationDict = new Dictionary<int, PlanningItemRelationCls>();

			_RelationPrevIndex = new IndexKCls<GID, PlanningItemRelationCls>();
			_RelationNextIndex = new IndexKCls<GID, PlanningItemRelationCls>();
			_RelationTypeIndex = new IndexKCls<PlanningItemRelationType, PlanningItemRelationCls>();
			_AxisSourceIndex = new Dictionary<GID, int>();
			_TaskOperIndex = new Dictionary<TwoInt32, int>();

			this._Status = new AxisHeapStatusCls();
			this.FinalMapCreated = false;
		}
		/// <summary> «Carrousel: YES»
		/// Prvotní úložiště dat jednotlivých položek materiálové osy
		/// </summary>
		internal Dictionary<int, MaterialPlanAxisItemCls> AxisS { get { return _AxisS; } }
		private Dictionary<int, MaterialPlanAxisItemCls> _AxisS;
		/// <summary> «Carrousel: YES»
		/// Prvotní úložiště dat jednotlivých položek kapacitních úkolů
		/// </summary>
		internal Dictionary<int, CapacityPlanWorkItemCls> TaskC { get { return _TaskC; } }
		private Dictionary<int, CapacityPlanWorkItemCls> _TaskC;
		/// <summary> «Carrousel: YES»
		/// Sekundární úložiště mapy všech jednotek práce
		/// </summary>
		internal Dictionary<int, WorkUnitCls> WorkD { get { return _WorkD; } }
		private Dictionary<int, WorkUnitCls> _WorkD;
		/// <summary> «Carrousel: YES»
		/// Vztahy mezi záznamy na ose a úkoly, i navzájem
		/// </summary>
		internal Dictionary<int, PlanningItemRelationCls> Relations { get { return _RelationDict; } }
		/// <summary> «Carrousel: YES»
		/// Úložiště vztahů. 
		/// Klíčem je ID vztahu, hodnotou jsou data vztahu.
		/// Klíče lze hledat podle indexů _RelationPrevIndex a _RelationNextIndex a _RelationTypeIndex.
		/// </summary>
		private Dictionary<int, PlanningItemRelationCls> _RelationDict;
		/// <summary> «Carrousel: YES»
		/// Index vztahů, kde klíčem je hodnota ItemGIDPrev, a hodnotou je seznam klíčů vztahů RelID
		/// </summary>
		private IndexKCls<GID, PlanningItemRelationCls> _RelationPrevIndex;
		/// <summary> «Carrousel: YES»
		/// Index vztahů, kde klíčem je hodnota ItemGIDNext, a hodnotou je seznam klíčů vztahů RelID
		/// </summary>
		private IndexKCls<GID, PlanningItemRelationCls> _RelationNextIndex;
		/// <summary> «Carrousel: YES»
		/// Index vztahů, kde klíčem je typ vztahu, a hodnotou je seznam klíčů vztahů RelID
		/// </summary>
		private IndexKCls<PlanningItemRelationType, PlanningItemRelationCls> _RelationTypeIndex;
		/// <summary> «Carrousel: YES»
		/// Index pro dohledání záznamu osy S podle záznamu vstupního (skladový doklad, výrobní příkaz).
		/// Key = GID vstupního záznamu (ProductOrder, WhDoc).
		/// Value = AxisID
		/// </summary>
		private Dictionary<GID, int> _AxisSourceIndex;
		/// <summary> «Carrousel: YES»
		/// Index pro dohledání záznamu tasku na základě ID osy a ID operace.
		/// Klíč = TwoInt (Axis:Operation).
		/// Value = TaskID.
		/// </summary>
		private Dictionary<TwoInt32, int> _TaskOperIndex;
		/// <summary> «Carrousel: YES»
		/// Stavové proměnné modulu AxisHeap, které jsou závislé na verzi plánu (se změnou plánu se aktivují jeho stavové proměnné)
		/// </summary>
		private AxisHeapStatusCls _Status;
		/// <summary>
		/// Příznak, zda již byla vygenerována finální mapa vztahů.
		/// </summary>
		internal bool FinalMapCreated { get { return _Status.FinalMapCreated; } set { _Status.FinalMapCreated = value; } }
		/// <summary>
		/// Obsahuje příznak, že aktuální data obsahují změny, které je třeba uložit do databáze.
		/// </summary>
		public bool ContainDirtyData { get { return _Status.ContainDirtyData; } set { _Status.ContainDirtyData = value; } }
		/// <summary>
		/// IDisposable
		/// </summary>
		public void Dispose()
		{
			if (this._AxisS != null) this._AxisS.Clear();
			if (this._TaskC != null) this._TaskC.Clear();
			if (this._RelationDict != null) this._RelationDict.Clear();
			if (this._RelationPrevIndex != null) this._RelationPrevIndex.Clear();
			if (this._RelationNextIndex != null) this._RelationNextIndex.Clear();
			if (this._RelationTypeIndex != null) this._RelationTypeIndex.Clear();
			if (this._RelationTypeIndex != null) this._RelationTypeIndex.Clear();
		}
		#endregion
		#region VERZE PLÁNU - PŘEPÍNÁNÍ. NAČÍTÁNÍ DAT Z DATABÁZE, INDEXOVÁNÍ
		/// <summary>
		/// Aktivace verze plánu
		/// </summary>
		/// <param name="planCVersion"></param>
		internal override void ActivateDataVersion(int planCVersion)
		{
			// Jak to funguje?   Popisek je v PlanningDataCls.ActivateDataVersion(), odkud se volá zdejší metoda.

			// Uložit současná data do současného karuselu:
			if (this.CurrentPlanCVersion != 0)
			{
				// Přetočit karusel na nová data (najít nebo vytvořit nový segment odpovídající číslu plánu):
				this.CarrouselSelectItem(this.CurrentPlanCVersion);

				// Uložit si do karuselu moje data, která jsou závislá na verzi plánu:
				this.CarrouselStoreDataToCurrent();
			}

			// Přetočit karusel na nová data (najít nebo vytvořit nový segment odpovídající číslu plánu):
			this.CarrouselSelectItem(planCVersion);

			// Vyzvednout z karuselu data nové verze (případně null, ale pak se bude volat DataPrepare):
			this._AxisS = this.CarrouselCurrentItem.Get<Dictionary<int, MaterialPlanAxisItemCls>>((int)CarrouselDataType.AxisS);
			this._TaskC = this.CarrouselCurrentItem.Get<Dictionary<int, CapacityPlanWorkItemCls>>((int)CarrouselDataType.TaskC);
			this._WorkD = this.CarrouselCurrentItem.Get<Dictionary<int, WorkUnitCls>>((int)CarrouselDataType.Works);
			this._RelationDict = this.CarrouselCurrentItem.Get<Dictionary<int, PlanningItemRelationCls>>((int)CarrouselDataType.Relations);

			this._RelationPrevIndex = this.CarrouselCurrentItem.Get<IndexKCls<GID, PlanningItemRelationCls>>((int)CarrouselDataType.RelationIdxPrev);
			this._RelationNextIndex = this.CarrouselCurrentItem.Get<IndexKCls<GID, PlanningItemRelationCls>>((int)CarrouselDataType.RelationIdxNext);
			this._RelationTypeIndex = this.CarrouselCurrentItem.Get<IndexKCls<PlanningItemRelationType, PlanningItemRelationCls>>((int)CarrouselDataType.RelationIdxType);
			this._AxisSourceIndex = this.CarrouselCurrentItem.Get<Dictionary<GID, int>>((int)CarrouselDataType.AxisIndexSource);
			this._TaskOperIndex = this.CarrouselCurrentItem.Get<Dictionary<TwoInt32, int>>((int)CarrouselDataType.TaskIndexOper);

			this._Status = this.CarrouselCurrentItem.Get<AxisHeapStatusCls>((int)CarrouselDataType.Status);

			// Zapamatovat si, že máme aktivní verzi plánu:
			this.CurrentPlanCVersion = planCVersion;
		}
		/// <summary>
		/// Uloží nynější datové objekty do aktuální položky karuselu (CarrouselCurrentItem).
		/// Virtuální metoda, určená k přepsání.
		/// Měla by se volat v průběhu aktivace verze dat z metody ActivateDataVersion() v její první části 
		///  (kdy ukládám nynější data do položky karuselu this.CurrentPlanCVersion),
		/// a pak by se měla volat z metody DataPrepare(), na jejím úplném konci, když jsou všechna data korektně připravena.
		/// Obsahem metody má být opakované volání this.CarrouselCurrentItem.Set(code, data) pro všechna data, která jsou závislá na verzi.
		/// </summary>
		protected override void CarrouselStoreDataToCurrent()
		{
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.AxisS, this._AxisS);
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.TaskC, this._TaskC);
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.Works, this._WorkD);
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.Relations, this._RelationDict);

			this.CarrouselCurrentItem.Set((int)CarrouselDataType.RelationIdxPrev, this._RelationPrevIndex);
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.RelationIdxNext, this._RelationNextIndex);
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.RelationIdxType, this._RelationTypeIndex);
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.AxisIndexSource, this._AxisSourceIndex);
			this.CarrouselCurrentItem.Set((int)CarrouselDataType.TaskIndexOper, this._TaskOperIndex);

			this.CarrouselCurrentItem.Set((int)CarrouselDataType.Status, this._Status);
		}
		/// <summary>
		/// Metoda je volaná na základě požadavku DataPrepare.
		/// Je volána tehdy, když se mají načíst číselníky a data, která jsou podkladem jak pro plánování, 
		/// tak pro načítání uložených dat plánu.
		/// </summary>
		/// <param name="withFixed">Požadavek na načítání i fixních dat, tj. takových, která nejsou závislá na verzi plánu.</param>
		internal override void DataPrepare(bool withFixed)
		{
			// Ve fázi DataPrepare se na osu nic nenačítá, ale budou se načítat potvrzené návrhy.

			// Veškeré datové objekty se zakládají jako new:
			this.DataCreate();

			// Uložím si aktuální stav dat (reference na datové objekty) do aktuální položky karuselu, což má být položka odpovídající aktuální verzi plánu:
			this.CarrouselStoreDataToCurrent();
		}
		/// <summary>
		/// Metoda je volaná na základě požadavku DataPrepare.
		/// Metodu si volá metoda DataPrepare().
		/// Je volána tehdy, když se načetly data do tabulek, a mají se vytvořit paměťové indexy.
		/// </summary>
		/// <param name="withFixed">Požadavek na indexování i fixních dat, tj. takových, která nejsou závislá na verzi plánu.</param>
		internal override void DataIndexing(bool withFixed)
		{
			this.NeedReIndexing = false;
		}
		/// <summary>
		/// Enum, který pojmenovává jednotlivé datové prvky, které tento modul ukládá do / načítá z karuselu.
		/// Enum slouží jako klíč k položkám karuselu.
		/// </summary>
		internal enum CarrouselDataType : int
		{
			Status = 1,
			AxisS,
			TaskC,
			Works,
			Relations,
			RelationIdxPrev,
			RelationIdxNext,
			RelationIdxType,
			AxisIndexSource,
			TaskIndexOper
		}
		#endregion
		#region VSTUP DAT NA PLÁNOVACÍ OSY
		/// <summary>
		/// Vymaže data z osy S a z tasků, a rovněž z dalších navázaných tabulek a indexů.
		/// </summary>
		internal void ClearPlanData()
		{
			this.AxisInit();
			this.TaskInit();
		}
		/// <summary>
		/// Iniciace plánovací osy před zahájením jejího plnění daty ze skladových dokladů a výrobních příkazů
		/// </summary>
		internal void AxisInit()
		{
			this._AxisS = new Dictionary<int, MaterialPlanAxisItemCls>();
			this._AxisSourceIndex = new Dictionary<GID, int>();
		}
		/// <summary>
		/// Iniciace seznamu úkolů a mapy vztahů před zahájením plnění daty
		/// </summary>
		internal void TaskInit()
		{
			this._TaskC = new Dictionary<int, CapacityPlanWorkItemCls>();
			this._WorkD = new Dictionary<int, WorkUnitCls>();
			this._RelationDict = new Dictionary<int, PlanningItemRelationCls>();
			this._RelationPrevIndex = new IndexKCls<GID, PlanningItemRelationCls>();
			this._RelationNextIndex = new IndexKCls<GID, PlanningItemRelationCls>();
			this._RelationTypeIndex = new IndexKCls<PlanningItemRelationType, PlanningItemRelationCls>();
			this._TaskOperIndex = new Dictionary<TwoInt32, int>();
			this.FinalMapCreated = false;
		}
		/// <summary>
		/// Přidá záznam osy S na plánovací osu S (ne do skladové karty).
		/// Plánovací osa je jednoznačné kompletní úložiště plánovacích dat.
		/// Na skladovou kartu je třeba přidat tento záznam volání metody this.ParentPlanning.MaterialData.AddPlanAxisSRecord().
		/// Vrací číslo záznamu osy S, pro diagnostické a testovací účely.
		/// </summary>
		/// <param name="axisItem">Data plánovací osy, která se přidává na materiálovou osu zásob</param>
		/// <param name="sourceGId">GID zdrojového záznamu. Podle tohoto GIDu lze poté najít záznam osy metodou AxisHeap.FindAxisSItemForSource(sourceGId).</param>
		/// <returns>Vrací číslo záznamu osy. Důvod: při testování se jako parametr vkládá nový záznam, jehož ID potřebuji vrátit.</returns>
		internal int AddPlanAxisSRecord(MaterialPlanAxisItemCls axisItem, GID sourceGId)
		{
			if (axisItem == null)
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o vložení záznamu materiálové osy, který je null."));
			
			if (_AxisS.ContainsKey(axisItem.AxisID))
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o duplicitní přidání záznamu na osu S, s klíčem %0.", axisItem.AxisID));
			
			this._AxisS.Add(axisItem.AxisID, axisItem);

			axisItem.SourceGId = sourceGId;
            if (sourceGId.ClassNumber != 0 && sourceGId.RecordNumber != 0)
			{	// Index pro klíč = zdrojový GID, hodnota = AxisID:
                if (this._AxisSourceIndex.ContainsKey(sourceGId))
                    Steward.TraceSaveLine("E", "AxisHeapCls", "AddPlanAxisSRecord", "Duplicity", "Chyba: pro vstupní záznam (GID=" + sourceGId.ToString() + ") je právě zakládán duplicitní záznam osy S.");
					// Throw.SysError(MessageInfo.Get("Chyba: pro vstupní záznam (GID=%0) je právě zakládán duplicitní záznam osy S.", sourceGId.ToString()));
                else
				    this._AxisSourceIndex.Add(sourceGId, axisItem.AxisID);
			}
			
			return axisItem.AxisID;
		}
		/// <summary>
		/// Odebere daný záznam z osy S.
		/// Odebere veškeré její vztahy ze systému vztahů (Relation).
		/// Neodebírá související záznam ze skladové karty, to musí řešit MaterialStock.
		/// Neodebírá Tasky.
		/// Vrací záznam osy, který byl odebrán.
		/// Pokud je zadán chybný záznam, nebo záznam neexistuje, vyhodí chybu.
		/// </summary>
		/// <param name="axisGId">Klíč záznamu osy S, který je třeba odebrat ze systému</param>
		internal MaterialPlanAxisItemCls RemovePlanAxisSRecord(GID axisGId)
		{
			if (axisGId.ClassNumber != Constants.ClassNumberAxis)
				Throw.SysError(MessageInfo.Get("Nelze odebrat záznam z osy S, předaný klíč není klíč pro třídu Osa S (GID = %0).", axisGId));

			MaterialPlanAxisItemCls axisItem;
			if (!this._AxisS.TryGetValue(axisGId.RecordNumber, out axisItem))
				Throw.AplError(MessageInfo.Get("Nelze odebrat záznam z osy S, požadovaný záznam neexistuje (GID = %0).", axisGId));
			
			// Odebrat z datového seznamu:
			this._AxisS.Remove(axisGId.RecordNumber);

			// Odebrat z indexu this._AxisSourceIndex (pokud je nenulová hodnota axisItem.SourceGId.ClassNumber):
			if (axisItem.SourceGId.ClassNumber != 0)
			{
				if (this._AxisSourceIndex.ContainsKey(axisItem.SourceGId))
					this._AxisSourceIndex.Remove(axisItem.SourceGId);
			}

			// Odebrat všechny vztahy z/na daný GID ze systému vztahů:
			this.RemoveItemRelationForGId(axisGId);

			// Vracím záznam osy S:
			return axisItem;
		}
		/// <summary>
		/// Přidá záznam kapacitního tasku  do systému - do soupisu kapacitních úkolů.
		/// Nezapisuje žádné vztahy.
		/// </summary>
		/// <param name="taskItem">Data kapacitního úkolu.</param>
		/// <returns>Vrací číslo záznamu tasku. Důvod: při testování se jako parametr vkládá nový záznam, jehož ID potřebuji vrátit.</returns>
		internal int AddPlanTaskCRecord(CapacityPlanWorkItemCls taskItem)
		{
			if (taskItem == null)
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o vložení záznamu kapacitního úkolu, který je null."));

			if (_TaskC.ContainsKey(taskItem.ItemID))
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o duplicitní přidání záznamu kapacitního úkolu, s klíčem %0.", taskItem.ItemID));

			// Záznam s daty:
			_TaskC.Add(taskItem.ItemID, taskItem);

			// Index AxisOper:
			TwoInt32 axisOper = new TwoInt32(taskItem.AxisID, taskItem.OperationKey);
			if (this._TaskOperIndex.ContainsKey(axisOper))
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o vložení záznamu s duplicitním klíčem Axis.Oper %0.", axisOper.ToString()));
			this._TaskOperIndex.Add(axisOper, taskItem.ItemID);

			return taskItem.ItemID;
		}
		/// <summary>
		/// Odebere daný záznam kapacitního úkolu ze seznamu tasků C.
		/// Odebere veškeré jeho vztahy ze systému vztahů (Relation).
		/// Neprovádí odregistraci kapacit, to musí řešit metoda this.CapacityRegisterData.UnRegisterCapacity().
		/// Neodebírá navázané záznamy osy S.
		/// Vrací záznam tasku, který byl odebrán.
		/// Pokud je zadán chybný záznam, nebo záznam neexistuje, vyhodí chybu.
		/// </summary>
		/// <param name="taskGId">Klíč záznamu tasku, který je třeba odebrat ze systému</param>
		internal CapacityPlanWorkItemCls RemovePlanTaskCRecord(GID taskGId)
		{
			if (taskGId.ClassNumber != Constants.ClassNumberTask)
				Throw.SysError(MessageInfo.Get("Nelze odebrat záznam kapacitního úkolu, předaný klíč není klíč pro třídu Task C (GID = %0).", taskGId));

			CapacityPlanWorkItemCls taskItem;
			if (!this._TaskC.TryGetValue(taskGId.RecordNumber, out taskItem))
				Throw.AplError(MessageInfo.Get("Nelze odebrat záznam kapacitního úkolu, požadovaný záznam neexistuje (GID = %0).", taskGId));

			// Odebrat z parent záznamu osy S:
			taskItem.AxisData.TaskRemove(taskItem);

			// Odebrat z datového seznamu:
			this._TaskC.Remove(taskGId.RecordNumber);

			// Odebrat všechny vztahy z/na daný GID ze systému vztahů:
			this.RemoveItemRelationForGId(taskGId);

			// Vracím záznam tasku:
			return taskItem;
		}
		/// <summary>
		/// Metoda, která je volaná typicky z průběhu registrace kapacit.
		/// Zajistí uložení záznamu WorkUnit do indexované tabulky AxisHeapCls._WorkD
		/// </summary>
		/// <param name="workUnit"></param>
		internal void AddWorkUnitRecord(WorkUnitCls workUnit)
		{
			if (workUnit == null)
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o vložení záznamu pracovního úkolu, který je null."));

			if (_WorkD.ContainsKey(workUnit.WorkID))
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o duplicitní přidání záznamu pracovního úkolu, s klíčem %0.", workUnit.WorkID));

			_WorkD.Add(workUnit.WorkID, workUnit);

			// Reset cache Linked List:
			this.ParentPlanning.LinkedElementListReset();
		}
		/// <summary>
		/// Metoda, která je volaná typicky z průběhu registrace kapacit.
		/// Zajistí odebrání záznamu WorkUnit z indexové tabulky AxisHeapCls._WorkD
		/// </summary>
		/// <param name="workUnit"></param>
		internal void RemoveWorkUnitRecord(WorkUnitCls workUnit)
		{
			if (workUnit == null)
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o odebrání záznamu pracovního úkolu, který je null."));

			if (!_WorkD.ContainsKey(workUnit.WorkID))
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o odebrání neexistujícího záznamu pracovního úkolu, s klíčem %0.", workUnit.WorkID));

			_WorkD.Remove(workUnit.WorkID);

			// Reset cache Linked List:
			this.ParentPlanning.LinkedElementListReset();
		}
		#endregion
		#region SPRÁVA VZTAHŮ MEZI ZÁZNAMY
		/// <summary>
		/// Přidá nový vztah mezi dvěma záznamy
		/// </summary>
		/// <param name="relationType">Typ vztahu</param>
		/// <param name="itemGIdPrev">ID předchozího záznamu</param>
		/// <param name="itemGIdNext">ID následujícího záznamu</param>
		/// <param name="linkedQty">Vázané množství</param>
		internal void AddItemRelation(PlanningItemRelationType relationType, GID itemGIdPrev, GID itemGIdNext, decimal linkedQty)
		{
			PlanningItemRelationCls relationItem = new PlanningItemRelationCls(relationType, itemGIdPrev, itemGIdNext, linkedQty);
			this._AddItemRelation(relationItem);
		}
		/// <summary>
		/// Vloží jeden další vztah
		/// </summary>
		/// <param name="relation">vztah</param>
		internal void AddItemRelation(PlanningItemRelationCls relation)
		{
			this._AddItemRelation(relation);
		}
		/// <summary>
		/// Vloží soupis vztahů
		/// </summary>
		/// <param name="relations">soupis vztahů</param>
		internal void AddItemRelationRange(IEnumerable<PlanningItemRelationCls> relations)
		{
			if (relations == null) return;

			foreach (PlanningItemRelationCls relation in relations)
				this._AddItemRelation(relation);
		}
		/// <summary>
		/// Vloží jeden další vztah. Privátní výkonná metoda.
		/// </summary>
		/// <param name="relation">Vztah mezi dvěma záznamy (typicky) nebo z jednoho záznamu do nikam (při balancování skladu).
		/// Výjimečně může být null, pak je třeba jej ignorovat.</param>
		private void _AddItemRelation(PlanningItemRelationCls relation)
		{
			if (relation == null) return;          // Vztah nebyl správně vypočítán.
			if (relation.IsEmpty) return;          // Vztah vede odnikud nikam.

			this._RelationDict.Add(relation.RelID, relation);
			this._RelationPrevIndex.Add(relation.ItemGIDPrev, relation.RelID, relation);
			this._RelationNextIndex.Add(relation.ItemGIDNext, relation.RelID, relation);
			this._RelationTypeIndex.Add(relation.Relation, relation.RelID, relation);
		}
		/// <summary>
		/// Odebere všechny vztahy, které se týkají daného GIDu, a to jak ze slovníku vztahů, tak z indexů.
		/// </summary>
		/// <param name="itemGId"></param>
		internal void RemoveItemRelationForGId(GID itemGId)
		{
			// Vztahy ve směru ToFuture, ToHistory::
			this._RemoveItemRelationsForGId(itemGId, TimeRange.TimeDirection.ToFuture);
			this._RemoveItemRelationsForGId(itemGId, TimeRange.TimeDirection.ToHistory);
		}
		/// <summary>
		/// Odebere ze systému vztahy z daného GIDu v daném směru.
		/// </summary>
		/// <param name="itemGId"></param>
		/// <param name="timeDir"></param>
		private void _RemoveItemRelationsForGId(GID itemGId, TimeRange.TimeDirection timeDir)
		{
			// Určím forward a reverzní index:
			IndexKCls<GID, PlanningItemRelationCls> relationForwardIndex = null;  // Index v požadovaném směru
			IndexKCls<GID, PlanningItemRelationCls> relationReverseIndex = null;  // Index reverzní k požadovaném směru
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:                            // Pro směr ToFuture :
					relationForwardIndex = this._RelationPrevIndex;               //  je Forward index ten, který pro klíč Prev vrátí vztahy doprava
					relationReverseIndex = this._RelationNextIndex;               //  je Reverse index ten, který pro klíč Next vrátí vztahy doleva
					break;
				case TimeRange.TimeDirection.ToHistory:                           // Pro směr ToHistory je tomu naopak.
					relationForwardIndex = this._RelationNextIndex;
					relationReverseIndex = this._RelationPrevIndex;
					break;
			}
			if (relationForwardIndex == null || relationReverseIndex == null)
				Throw.SysError(MessageInfo.Get("Chyba při určení směru v odstraňování vztahů: timeDir = %0.", timeDir));

			//  Poznámky, příklad:
			// Pokud mám vztahy dejme tomu:  10-20; 10-30; 10-40; 15-20, 15-10 pak jsou tyto vztahy (pod svým soukromým RelID) uloženy v this._RelationDict.
			// V indexu PREV jsou tyto záznamy: 10-{10-20, 10-30, 10-40); 15-{15-10, 15-20}  (pro shodný klíč = Prev je uveden seznam hodnot = Next).
			// V indexu NEXT jsou tyto záznamy: 10-{15-10}; 20-{10-20, 15-20}; 30-{10-30}; 40-{10-40}.
			// Zkusme smazat všechny vztahy klíče 10:
			// Při mazání ToFuture bude Forward index = PREV a Reverse index = NEXT
			//   hlavní seznam vztahů z Forward indexu bude 10-{10-20, 10-30, 10-40), ten se smaže na konci této metody (relationForwardIndex.RemoveKey(itemGId)).
			//   Procházím tedy skrz vztahy 10-20, 10-30, 10-40 (foreach (PlanningItemRelationCls relItem in relList))
			//   určím reverzní GID (ToFuture určí postupně GIDy 20, 30, 40) (viz relItem.ItemGIDEdge(timeDir, false))
			//   a smažu je jako jednotlivé položky z reverzního indexu (NEXT), 
			//     tedy smažu z indexu NEXT z klíče 20 položku 10-20, z klíče 30 položku 10-30, z klíče 40 položku 10-40
			//   pak smažu jednotlivé vztahy, a na konec smažu celý klíč 10 z indexu PREV (10-{10-20, 10-30, 10-40)).
			// V dalším kroku budu mazat ve směru ToHistory, Forward index = NEXT a Reverse index = PREV
			//   hlavní seznam vztahů z Forward indexu NEXT bude 10-{15-10}
			//   Procházím tedy skrz vztahy 15-10
			//   určím reverzní GID (ToHistory najde GID 15)
			//   a smažu je jako jednotlivé položky z reverzního indexu (PREV), 
			//     tedy smažu z indexu PREV z klíče 15 položku 15-10
			//   pak smažu jednotlivé vztahy, a na konec smažu celý klíč 10 z indexu NEXT 10-{15-10}.

			// Jednoduché hledání ve Forward indexu: pro klíč vrátí hodnoty.
			List<PlanningItemRelationCls> relList = relationForwardIndex.GetData(itemGId);
			foreach (PlanningItemRelationCls relItem in relList)
			{
				// Odeberu položku z indexu Reverse (tam se bude vyskytovat jako jedna z hodnot pro reverzní klíč):
				GID itemReverseGId = relItem.ItemGIDEdge(timeDir, false);         // Hodnota GID z opačné strany vztahu (Pokud hledám vztahy pro GID 10, a najdu vztah { 10 - 25 }, pak reverzní GID je 25).
				relationReverseIndex.RemoveItem(itemReverseGId, relItem.RelID);   // Odeberu jednu položku z listu Values z reverzního indexu (v příkladu z předešlého řádku to bude položka pro Key = 25, Value = 10)

				// Odeberu položku z indexu _RelationTypeIndex 
				//  (obsahuje pro daný typ vztahu všechny vztahy, odeberu jednu položku daného RelID):
				this._RelationTypeIndex.RemoveItem(relItem.Relation, relItem.RelID);

				// Odeberu data vlastního vztahu:
				if (this._RelationDict.ContainsKey(relItem.RelID))
					this._RelationDict.Remove(relItem.RelID);
			}

			// Odeberu celý záznam pro daný klíč z Forward indexu (tj. seznam všech Values):
			relationForwardIndex.RemoveKey(itemGId);
		}
		/// <summary>
		/// Metoda odebere ze systému vztahů jeden vztah podle jeho RelID
		/// </summary>
		/// <param name="relIDList">Soupis RelID vztahů ke smazání</param>
		internal void RemoveItemRelationForRelID(IEnumerable<int> relIDList)
		{
			if (relIDList == null) return;
			foreach (int relID in relIDList)
				this.RemoveItemRelationForRelID(relID);
		}
		/// <summary>
		/// Metoda odebere ze systému vztahů jeden vztah podle jeho RelID
		/// </summary>
		/// <param name="relItemList">Soupis dat vztahů ke smazání</param>
		internal void RemoveItemRelationForRelID(IEnumerable<PlanningItemRelationCls> relItemList)
		{
			if (relItemList == null) return;
			foreach (PlanningItemRelationCls relItem in relItemList)
				this._RemoveItemRelationForRelID(relItem);
		}
		/// <summary>
		/// Metoda odebere ze systému vztahů jeden vztah podle jeho RelID
		/// </summary>
		/// <param name="relID">Klíč vztahu = RelID</param>
		internal void RemoveItemRelationForRelID(int relID)
		{
			PlanningItemRelationCls relItem;
			if (this._RelationDict.TryGetValue(relID, out relItem))
				this._RemoveItemRelationForRelID(relItem);
		}
		/// <summary>
		/// Metoda odebere ze systému vztahů jeden vztah podle jeho RelID
		/// </summary>
		/// <param name="relItem">Data o vztahu</param>
		internal void RemoveItemRelationForRelID(PlanningItemRelationCls relItem)
		{
			if (relItem != null)
				this._RemoveItemRelationForRelID(relItem);
		}
		/// <summary>
		/// Odebere daný vztah ze systému vztahů (z this._RelationDict a z indexů)
		/// </summary>
		/// <param name="relItem"></param>
		private void _RemoveItemRelationForRelID(PlanningItemRelationCls relItem)
		{
			if (relItem == null) return;

			this._RelationPrevIndex.RemoveItem(relItem.ItemGIDPrev, relItem.RelID);     // Odeberu jednu položku z indexu Prev (klíčem je GIDPrev, odeberu z něj záznam s daným RelID)
			this._RelationNextIndex.RemoveItem(relItem.ItemGIDNext, relItem.RelID);
			this._RelationTypeIndex.RemoveItem(relItem.Relation, relItem.RelID);
			if (this._RelationDict.ContainsKey(relItem.RelID))
				this._RelationDict.Remove(relItem.RelID);
		}
		/// <summary>
		/// Vrátí soupis klíčů RelID vztahů.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdPrev">GID předchozí položky</param>
		/// <returns></returns>
		internal List<int> FindRelationNextIDKeys(GID itemGIdPrev)
		{
			return this._RelationPrevIndex.GetValues(itemGIdPrev);
		}
		/// <summary>
		/// Vrátí soupis klíčů RelID vztahů.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdNext">GID následující položky</param>
		/// <returns></returns>
		internal List<int> FindRelationPrevIDKeys(GID itemGIdNext)
		{
			return this._RelationNextIndex.GetValues(itemGIdNext);
		}
		/// <summary>
		/// Vrátí soupis vztahů na následující položky, na základě GID položky Prev.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdPrev">GID předchozí položky</param>
		/// <returns></returns>
		internal List<PlanningItemRelationCls> FindRelationNextIDData(GID itemGIdPrev)
		{
			return this._RelationPrevIndex.GetData(itemGIdPrev);
		}
		/// <summary>
		/// Vrátí soupis vztahů na následující položky, na základě GID položky Prev.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdPrev">GID předchozí položky</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns></returns>
		internal List<PlanningItemRelationCls> FindRelationNextIDData(GID itemGIdPrev, Predicate<PlanningItemRelationCls> predicate)
		{
			if (predicate == null)
				return this._RelationPrevIndex.GetData(itemGIdPrev);

			List<PlanningItemRelationCls> relList = this._RelationPrevIndex.GetData(itemGIdPrev);
			List<PlanningItemRelationCls> result = new List<PlanningItemRelationCls>();
			foreach (PlanningItemRelationCls relItem in relList)
			{
				if (predicate(relItem))
					result.Add(relItem);
			}
			return result;
		}
		/// <summary>
		/// Vrátí soupis vztahů na předchozí položky, na základě GID položky Next.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdNext">GID následující položky</param>
		/// <returns></returns>
		internal List<PlanningItemRelationCls> FindRelationPrevIDData(GID itemGIdNext)
		{
			return this._RelationNextIndex.GetData(itemGIdNext);
		}
		/// <summary>
		/// Vrátí soupis vztahů na předchozí položky, na základě GID položky Next.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdNext">GID následující položky</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns></returns>
		internal List<PlanningItemRelationCls> FindRelationPrevIDData(GID itemGIdNext, Predicate<PlanningItemRelationCls> predicate)
		{
			if (predicate == null)
				return this._RelationNextIndex.GetData(itemGIdNext);

			List<PlanningItemRelationCls> relList = this._RelationNextIndex.GetData(itemGIdNext);
			List<PlanningItemRelationCls> result = new List<PlanningItemRelationCls>();
			foreach (PlanningItemRelationCls relItem in relList)
			{
				if (predicate(relItem))
					result.Add(relItem);
			}
			return result;
		}
		/// <summary>
		/// Vrátí soupis finálních vztahů, tj takové, které které už nemají žádné další návazné vztahy.
		/// Poslední je ten, kdo ukazuje na záznam Next, jehož RecordNumber je 0 (ClassNumber může být nenulové).
		/// </summary>
		/// <param name="itemGIdPrev">GID záznamu, který je některým z předchůdců hledaného (hledaných) finálních záznamů</param>
		/// <returns>Soupis GIDů položek, které jsou finálními položkami (z nichž už cesta dál nevede)</returns>
		internal List<GID> FindRelationLastIDData(GID itemGIdPrev)
		{
			return this._FindRelationLastIDData(itemGIdPrev, null);
		}
		/// <summary>
		/// Vrátí soupis finálních vztahů, tj takové, které které už nemají žádné další návazné vztahy.
		/// Poslední je ten, kdo ukazuje na záznam Next, jehož RecordNumber je 0 (ClassNumber může být nenulové).
		/// </summary>
		/// <param name="itemGIdPrev">GID záznamu, který je některým z předchůdců hledaného (hledaných) finálních záznamů</param>
		/// <param name="predicate">Přídavný filtr na finální položky, které se ukládají do výstupního seznamu</param>
		/// <returns>Soupis GIDů položek, které jsou finálními položkami (z nichž už cesta dál nevede)</returns>
		internal List<GID> FindRelationLastIDData(GID itemGIdPrev, Predicate<PlanningItemRelationCls> predicate)
		{
			return this._FindRelationLastIDData(itemGIdPrev, predicate);
		}
		/// <summary>
		/// Vrátí soupis finálních záznamů, tj takové, které už nemají žádné další návazné vztahy.
		/// Poslední je ten, kdo ukazuje na záznam Next, jehož RecordNumber je 0 (ClassNumber může být nenulové).
		/// </summary>
		/// <param name="itemGIdPrev">GID záznamu, který je některým z předchůdců hledaného (hledaných) finálních záznamů</param>
		/// <param name="predicate">Přídavný filtr na finální položky, které se ukládají do výstupního seznamu</param>
		/// <returns>Soupis GIDů položek, které jsou finálními položkami (z nichž už cesta dál nevede)</returns>
		private List<GID> _FindRelationLastIDData(GID itemGIdPrev, Predicate<PlanningItemRelationCls> predicate)
		{
			Dictionary<GID, object> result = new Dictionary<GID,object>();  // Soupis nalezených finálních bodů
			Queue<GID> queue = new Queue<GID>();                            // Fronta položek, které ještě projdeme
			queue.Enqueue(itemGIdPrev);                                     // Do zásobníku dám první položku = požadavek
			Hashtable gidHt = new Hashtable();                              // Antiduplicitní soupis: když přijdu na určitý GID z jiné podřízené cesty, nebudu jej procházet podruhé.
			bool withPredicate = (predicate != null);
			while (queue.Count > 0)
			{
				GID itemGId = queue.Dequeue();                              // Další klíč ke zpracování. Pokud jsem jej už dělal, přeskočím jej.
				if (gidHt.ContainsKey(itemGId)) continue;
				gidHt.Add(itemGId, null);

				List<PlanningItemRelationCls> relList = this._RelationPrevIndex.GetData(itemGId);   // Následnící daného uzlu
				foreach (PlanningItemRelationCls relItem in relList)
				{	// Všechny vztahy: podle toho, co je vpravo:
					if (relItem.ItemGIDNext.RecordNumber == 0)
					{	// Vpravo nic není: pak ten prvek, který je vlevo přidám do výsledného soupisu:
						bool add = (!withPredicate ? true : (predicate(relItem)));   // Přídavný filtr na položky, které dávám do výstupního seznamu
						if (add && !result.ContainsKey(itemGId))
							result.Add(itemGId, null);
					}
					else
						// Vpravo něco je: přidám to do zásobníku položek pro další chody:
						queue.Enqueue(relItem.ItemGIDNext);
				}
			}
			return new List<GID>(result.Keys);
		}
		/// <summary>
		/// Vrátí soupis vztahů na sousední položky v daném směru, na základě GID výchozí položky.
		/// Daný směr: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null, kromě zadání nesprávného směru času.
		/// </summary>
		/// <param name="timeDir">Směr hledání sousedních položek: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.</param>
		/// <param name="itemGIdBase">GID výchozí položky, jejíž sousední vztahy hledám</param>
		/// <returns>Soupis záznamů vztahů</returns>
		internal List<PlanningItemRelationCls> FindRelationOtherIDData(TimeRange.TimeDirection timeDir, GID itemGIdBase)
		{
			return this._FindRelationOtherIDData(timeDir, false, itemGIdBase, null);
		}
		/// <summary>
		/// Vrátí soupis vztahů na sousední položky v daném směru, na základě GID výchozí položky.
		/// Daný směr: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null, kromě zadání nesprávného směru času.
		/// </summary>
		/// <param name="timeDir">Směr hledání sousedních položek: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.</param>
		/// <param name="itemGIdBase">GID výchozí položky, jejíž sousední vztahy hledám</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns>Soupis záznamů vztahů</returns>
		internal List<PlanningItemRelationCls> FindRelationOtherIDData(TimeRange.TimeDirection timeDir, GID itemGIdBase, Predicate<PlanningItemRelationCls> predicate)
		{
			return this._FindRelationOtherIDData(timeDir, false, itemGIdBase, predicate);
		}
		/// <summary>
		/// Vrátí soupis vztahů na sousední položky v daném směru, na základě GID výchozí položky.
		/// Daný směr: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null, kromě zadání nesprávného směru času.
		/// </summary>
		/// <param name="timeDir">Směr hledání sousedních položek: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.</param>
		/// <param name="reverse">Obraceč směru času. Pokud je true, pak pro směr ToFuture vrací Prev a pro směr ToHistory vrací položky Next.</param>
		/// <param name="itemGIdBase">GID výchozí položky, jejíž sousední vztahy hledám</param>
		/// <returns>Soupis záznamů vztahů</returns>
		internal List<PlanningItemRelationCls> FindRelationOtherIDData(TimeRange.TimeDirection timeDir, bool reverse, GID itemGIdBase)
		{
			return this._FindRelationOtherIDData(timeDir, reverse, itemGIdBase, null);
		}
		/// <summary>
		/// Vrátí soupis vztahů na sousední položky v daném směru, na základě GID výchozí položky.
		/// Daný směr: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null, kromě zadání nesprávného směru času.
		/// </summary>
		/// <param name="timeDir">Směr hledání sousedních položek: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.</param>
		/// <param name="reverse">Obraceč směru času. Pokud je true, pak pro směr ToFuture vrací Prev a pro směr ToHistory vrací položky Next.</param>
		/// <param name="itemGIdBase">GID výchozí položky, jejíž sousední vztahy hledám</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns>Soupis záznamů vztahů</returns>
		internal List<PlanningItemRelationCls> FindRelationOtherIDData(TimeRange.TimeDirection timeDir, bool reverse, GID itemGIdBase, Predicate<PlanningItemRelationCls> predicate)
		{
			return this._FindRelationOtherIDData(timeDir, reverse, itemGIdBase, predicate);
		}
		/// <summary>
		/// Vrátí soupis vztahů na sousední položky v daném směru, na základě GID výchozí položky.
		/// Daný směr: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null, kromě zadání nesprávného směru času.
		/// </summary>
		/// <param name="timeDir">Směr hledání sousedních položek: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.</param>
		/// <param name="reverse">Obraceč směru času. Pokud je true, pak pro směr ToFuture vrací Prev a pro směr ToHistory vrací položky Next.</param>
		/// <param name="itemGIdBase">GID výchozí položky, jejíž sousední vztahy hledám</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns>Soupis záznamů vztahů</returns>
		private List<PlanningItemRelationCls> _FindRelationOtherIDData(TimeRange.TimeDirection timeDir, bool reverse, GID itemGIdBase, Predicate<PlanningItemRelationCls> predicate)
		{
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					if (!reverse)
						return this.FindRelationNextIDData(itemGIdBase, predicate);
					else
						return this.FindRelationPrevIDData(itemGIdBase, predicate);
				case TimeRange.TimeDirection.ToHistory:
					if (!reverse)
						return this.FindRelationPrevIDData(itemGIdBase, predicate);
					else
						return this.FindRelationNextIDData(itemGIdBase, predicate);
			}
			return null;
		}
		/// <summary>
		/// Vrátí soupis všech vztahů daného typu.
		/// Pokud nenajde záznam pro hledaný typ, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="relType">Typ vztahu</param>
		/// <returns>Nalezené záznamy vztahů, seznam</returns>
		internal List<PlanningItemRelationCls> FindRelationTypeData(PlanningItemRelationType relType)
		{
			return this._RelationTypeIndex.GetData(relType);
		}
		/// <summary>
		/// Vrátí soupis všech vztahů daného typu.
		/// Pokud nenajde záznam pro hledaný typ, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="relType">Typ vztahu</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns>Nalezené záznamy vztahů, seznam</returns>
		internal List<PlanningItemRelationCls> FindRelationTypeData(PlanningItemRelationType relType, Predicate<PlanningItemRelationCls> predicate)
		{
			if (predicate == null)
				return this._RelationTypeIndex.GetData(relType);

			List<PlanningItemRelationCls> relList = this._RelationTypeIndex.GetData(relType);
			List<PlanningItemRelationCls> result = new List<PlanningItemRelationCls>();
			foreach (PlanningItemRelationCls relItem in relList)
			{
				if (predicate(relItem))
					result.Add(relItem);
			}
			return result;
		}
		/// <summary>
		/// Vrátí soupis všech vztahů daných typů.
		/// Pokud nenajde záznam pro hledaný typ, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="relTypes">Typy vztahu</param>
		/// <returns>Nalezené záznamy vztahů, seznam</returns>
		internal List<PlanningItemRelationCls> FindRelationTypesData(params PlanningItemRelationType[] relTypes)
		{
			if (relTypes == null || relTypes.Length == 0) return new List<PlanningItemRelationCls>();
			if (relTypes.Length == 1) return FindRelationTypeData(relTypes[0]);

			List<PlanningItemRelationCls> result = new List<PlanningItemRelationCls>();
			foreach (PlanningItemRelationType relType in relTypes)
				result.AddRange(this.FindRelationTypeData(relType));

			return result;
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů osy S, které stojí vlevo (Prev) ve vztazích od daného (Next).
		/// Vybírá pouze vztahy, kde vlevo je GID čísla třídy AxisS.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdNext">GID následující položky</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns></returns>
		internal List<MaterialPlanAxisItemCls> FindRelationPrevAxisSList(GID itemGIdNext)
		{
			return this.FindRelationPrevAxisSList(itemGIdNext, null);
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů osy S, které stojí vlevo (Prev) ve vztazích od daného (Next).
		/// Vybírá pouze vztahy, kde vlevo je GID čísla třídy AxisS.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdNext">GID následující položky</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns></returns>
		internal List<MaterialPlanAxisItemCls> FindRelationPrevAxisSList(GID itemGIdNext, Predicate<PlanningItemRelationCls> predicate)
		{
			List<PlanningItemRelationCls> relations = this.FindRelationPrevIDData(itemGIdNext, predicate);
			IEnumerable<GID> gidList = relations.ConvertAll(r => r.ItemGIDPrev).Where(g => g.ClassNumber == Constants.ClassNumberAxis);
			List<MaterialPlanAxisItemCls> result = this.FindAxisSItemList(gidList);
			return result;
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů osy S, které stojí vpravo (Next) ve vztazích od daného (Prev).
		/// Vybírá pouze vztahy, kde vpravo je GID čísla třídy AxisS.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdPrev">GID následující položky</param>
		/// <returns></returns>
		internal List<MaterialPlanAxisItemCls> FindRelationNextAxisSList(GID itemGIdPrev)
		{
			return this.FindRelationNextAxisSList(itemGIdPrev, null);
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů osy S, které stojí vpravo (Next) ve vztazích od daného (Prev).
		/// Vybírá pouze vztahy, kde vpravo je GID čísla třídy AxisS.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdPrev">GID následující položky</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns></returns>
		internal List<MaterialPlanAxisItemCls> FindRelationNextAxisSList(GID itemGIdPrev, Predicate<PlanningItemRelationCls> predicate)
		{
			List<PlanningItemRelationCls> relations = this.FindRelationNextIDData(itemGIdPrev, predicate);
			IEnumerable<GID> gidList = relations.ConvertAll(r => r.ItemGIDNext).Where(g => g.ClassNumber == Constants.ClassNumberAxis);
			List<MaterialPlanAxisItemCls> result = this.FindAxisSItemList(gidList);
			return result;
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů tasků C, které stojí vlevo (Prev) ve vztazích od daného (Next).
		/// Vybírá pouze vztahy, kde vlevo je GID čísla třídy TaskC.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdNext">GID následující položky</param>
		/// <returns></returns>
		internal List<CapacityPlanWorkItemCls> FindRelationPrevTaskCList(GID itemGIdNext)
		{
			return this.FindRelationPrevTaskCList(itemGIdNext, null);
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů tasků C, které stojí vlevo (Prev) ve vztazích od daného (Next).
		/// Vybírá pouze vztahy, kde vlevo je GID čísla třídy TaskC.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdNext">GID následující položky</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns></returns>
		internal List<CapacityPlanWorkItemCls> FindRelationPrevTaskCList(GID itemGIdNext, Predicate<PlanningItemRelationCls> predicate)
		{
			List<PlanningItemRelationCls> relations = this.FindRelationPrevIDData(itemGIdNext, predicate);
			IEnumerable<GID> gidList = relations.ConvertAll(r => r.ItemGIDPrev).Where(g => g.ClassNumber == Constants.ClassNumberTask);
			List<CapacityPlanWorkItemCls> result = this.FindTaskCItemList(gidList);
			return result;
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů tasků C, které stojí vpravo (Next) ve vztazích od daného (Prev).
		/// Vybírá pouze vztahy, kde vpravo je GID čísla třídy TaskC.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdPrev">GID následující položky</param>
		/// <returns></returns>
		internal List<CapacityPlanWorkItemCls> FindRelationNextTaskCList(GID itemGIdPrev)
		{
			return this.FindRelationNextTaskCList(itemGIdPrev, null);
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů tasků C, které stojí vpravo (Next) ve vztazích od daného (Prev).
		/// Vybírá pouze vztahy, kde vpravo je GID čísla třídy TaskC.
		/// Pokud nenajde záznam pro hledaný klíč, vrátí prázdný seznam. Nevrací null.
		/// </summary>
		/// <param name="itemGIdPrev">GID následující položky</param>
		/// <param name="predicate">Predikát, který musí splnit vztahy, které budou vráceny ve výstupním poli</param>
		/// <returns></returns>
		internal List<CapacityPlanWorkItemCls> FindRelationNextTaskCList(GID itemGIdPrev, Predicate<PlanningItemRelationCls> predicate)
		{
			List<PlanningItemRelationCls> relations = this.FindRelationNextIDData(itemGIdPrev, predicate);
			IEnumerable<GID> gidList = relations.ConvertAll(r => r.ItemGIDNext).Where(g => g.ClassNumber == Constants.ClassNumberTask);
			List<CapacityPlanWorkItemCls> result = this.FindTaskCItemList(gidList);
			return result;
		}
        /// <summary>
        /// Metoda najde a vrátí seznam klíčů (GID) záznamů osy S, které reprezentují finální záznamy, do nichž (Next) doputuje něco z uvedeného GIDu (ať už je GIDem cokoliv: Axis nebo Task).
        /// </summary>
        /// <param name="itemGId"></param>
        /// <returns></returns>
        internal List<GID> FindRelationFinalAxisSGIds(GID itemGId)
        {
            List<PlanningItemRelationCls> finalRelations = this._FindRelationRecursive(itemGId, TimeRange.TimeDirection.ToFuture, false,
                rel => (rel.ItemGIDNext.ClassNumber != Constants.ClassNumberOutput && rel.ItemGIDNext.ClassNumber != 0),
                rel => (rel.ItemGIDNext.ClassNumber == Constants.ClassNumberOutput));
            return finalRelations.ConvertAll(rel => rel.ItemGIDPrev);
        }
        /// <summary>
        /// Obecná metoda pro vyhledání soupisu vztahů z daného výchozího místa, daným směrem a se specifikací podmínek.
        /// Metoda najde vztahy daným směrem z výchozího místa, každý vztah předá do predikátu "goNext", který rozhodne zda pro tento vztah budeme prohledávat i jeho následníky.
        /// Každý vztah rovněž předá do predikátu "isResult", který rozhodne zda daný vztah se uloží do výstupu.
        /// Volající tedy může definií predikátů řídit chod tohoto "pavouka" i řídit jeho "sběračské" vlastnosti.
        /// </summary>
        /// <param name="initialGId"></param>
        /// <param name="timeDir"></param>
        /// <param name="goNext"></param>
        /// <param name="isResult"></param>
        /// <returns></returns>
        private List<PlanningItemRelationCls> _FindRelationRecursive(GID initialGId, TimeRange.TimeDirection timeDir, bool timeReverse, Predicate<PlanningItemRelationCls> goNext, Predicate<PlanningItemRelationCls> isResult)
        {
            Queue<GID> work = new Queue<GID>();
            Dictionary<GID, object> scanned = new Dictionary<GID, object>();
            Dictionary<int, PlanningItemRelationCls> result = new Dictionary<int,PlanningItemRelationCls>();

            work.Enqueue(initialGId);
            scanned.Add(initialGId, null);
            while (work.Count > 0)
            {   // V tomhle cyklu vždy jednu položku (GID) z fronty odeberu a zpracuji, několik (0 ÷ nn) položek do fronty přidám. Skončím, až bude fronta prázdná.
                // Do fronty přidávám jen to, co v ní dosud nebylo (což mi hlídá scanned).
                GID stepGId = work.Dequeue();
                List<PlanningItemRelationCls> relations = this.FindRelationOtherIDData(timeDir, timeReverse, stepGId);
                foreach (PlanningItemRelationCls relation in relations)
                {
                    if (goNext(relation))
                    {   // Pokud daný vztah vyhovuje podmínce pro další scanování (predikát goNext), zařadíme si klíč GID z následující strany vztahu (timeDir, timeReverse) do soupisu k další práci:
                        GID nextGId = relation.ItemGIDEdge(timeDir, timeReverse);
                        if (nextGId.NotEmpty && !scanned.ContainsKey(nextGId))
                        {
                            work.Enqueue(nextGId);
                            scanned.Add(nextGId, null);
                        }
                    }
                    if (isResult(relation))
                    {   // Pokud daný vztah vyhovuje podmínce pro zařazení do výsledku (predikát isResult), zařadíme tento vztah do resultu:
                        if (!result.ContainsKey(relation.RelID))
                            result.Add(relation.RelID, relation);
                    }
                }
            }
            return new List<PlanningItemRelationCls>(result.Values);
        }
		#endregion
		#region ZÁKLADNÍ VYHLEDÁNÍ DAT Z PLÁNOVACÍCH OS
		/// <summary>
		/// Najde a vrátí záznam materiálové osy podle jeho GID.
		/// Zkontroluje správné číslo třídy v požadovaném GIDu, v případě nesprávného čísla třídy vyhodí chybu.
		/// Pokud záznam osy podle daného čísla nenajde, vrací null.
		/// </summary>
        /// <param name="axisGID">GID záznamu osy S. Kontroluje se shoda čísla třídy (musí být MaterialPlanAxisItemCls.ClassNr = Constants.ClassNumberAxis = 0x10006 anebo Constants.ClassNumberAxisSubRow =  0x10009)</param>
		/// <returns></returns>
		public MaterialPlanAxisItemCls FindAxisSItem(GID axisGID)
		{
            if (axisGID.ClassNumber == MaterialPlanAxisItemCls.ClassNr || axisGID.ClassNumber == Constants.ClassNumberAxisSubRow)
                return this.FindAxisSItem(axisGID.RecordNumber);
            
			Throw.SysError(MessageInfo.Get("Pokus o nalezení záznamu typu Axis, jehož GID odkazuje na neplatnou třídu (požadavek=%0, povolená hodnota=%1).", axisGID.ClassNumber, MaterialPlanAxisItemCls.ClassNr));
            return null;        // for compiler
		}
		/// <summary>
		/// Najde a vrátí záznam materiálové osy podle jeho ID
		/// Pokud záznam osy podle daného čísla nenajde, vrací null.
		/// </summary>
		/// <param name="axisID">ID záznamu osy.</param>
		/// <returns></returns>
		public MaterialPlanAxisItemCls FindAxisSItem(int axisID)
		{
			MaterialPlanAxisItemCls result;
			if (this._AxisS.TryGetValue(axisID, out result))
				return result;
			return null;
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů osy S, které jsou požadovány v dodaném soupisu klíčů GID.
		/// Klíče mohou být z různých tříd (GID.ClassNumber), tato metoda vybírá pouze položky adresující (číslem třídy) osu S.
		/// Pokud je předán parametr == null, vrací null. Jinak vrací List, může být i prázdný.
		/// Pokud je některý GID uveden ve vstupním seznamu vícekrát, bude záznam osy S vícekrát i ve výstupním seznamu (neřeší se duplicita).
		/// Pořadí výstupního seznamu odpovídá vstupnímu seznamu.
		/// Ve výstupním seznamu nejsou položky pro vstupující položky z jiných tříd, nebo neexistující záznamy (výstupní seznam neobsahuje položky == null).
		/// </summary>
		/// <param name="axisGIDs"></param>
		/// <returns></returns>
		public List<MaterialPlanAxisItemCls> FindAxisSItemList(IEnumerable<GID> axisGIDs)
		{
			if (axisGIDs == null) return null;
			List<MaterialPlanAxisItemCls> result = new List<MaterialPlanAxisItemCls>();
			foreach (GID gid in axisGIDs.Where(g => (g.ClassNumber == MaterialPlanAxisItemCls.ClassNr || g.ClassNumber == Constants.ClassNumberAxisSubRow)))
			{
				MaterialPlanAxisItemCls axisData;
				if (this._AxisS.TryGetValue(gid.RecordNumber, out axisData))
					result.Add(axisData);
			}
			return result;
		}
		/// <summary>
		/// Najde a vrátí záznam materiálové osy podle GID jeho zdroje.
		/// Touto cestou lze najít záznam osy, který je založen na základě externího dokladu (skladový doklad, výrobní příkaz).
		/// Nelze najít návazné vnitřní doklady, které jsou od vstupního dokladu odvozeny (například zajištění nebo rozpady).
		/// </summary>
		/// <param name="sourceGId">GID vnějšího záznamu (Wh, ProductOrder).</param>
		/// <returns></returns>
		public MaterialPlanAxisItemCls FindAxisSItemForSource(GID sourceGId)
		{
			int axisID;
			if (!this._AxisSourceIndex.TryGetValue(sourceGId, out axisID)) return null;
			return this.FindAxisSItem(axisID);
		}
		/// <summary>
		/// Najde a vrátí seznam záznamů kapacitních úkolů podle seznamu GID.
		/// Zkontroluje správné číslo třídy v požadovaném GIDu, v případě nesprávného čísla třídy vyhodí chybu.
		/// </summary>
		/// <param name="taskGIDs">Soupis GID kapacitního úkolu. Kontroluje se shoda čísla třídy (musí být CapacityPlanWorkItemCls.ClassNr = Constants.ClassNumberTask = 0x10001)</param>
		/// <returns>Data kapacitního úkolu</returns>
		public List<CapacityPlanWorkItemCls> FindTaskCItemList(IEnumerable<GID> taskGIDs)
		{
			if (taskGIDs == null) return null;
			List<CapacityPlanWorkItemCls> result = new List<CapacityPlanWorkItemCls>();
			foreach (GID gid in taskGIDs.Where(g => (g.ClassNumber == CapacityPlanWorkItemCls.ClassNr || g.ClassNumber == Constants.ClassNumberTask)))
			{
				CapacityPlanWorkItemCls taskData;
				if (this._TaskC.TryGetValue(gid.RecordNumber, out taskData))
					result.Add(taskData);
			}
			return result;
		}
		/// <summary>
		/// Najde a vrátí záznam kapacitního úkolu podle jeho GID.
		/// Zkontroluje správné číslo třídy v požadovaném GIDu, v případě nesprávného čísla třídy vyhodí chybu.
		/// </summary>
		/// <param name="taskGID">GID kapacitního úkolu. Kontroluje se shoda čísla třídy (musí být CapacityPlanWorkItemCls.ClassNr = Constants.ClassNumberTask = 0x10001)</param>
		/// <returns>Data kapacitního úkolu</returns>
		public CapacityPlanWorkItemCls FindTaskCItem(GID taskGID)
		{
			if (taskGID.ClassNumber != CapacityPlanWorkItemCls.ClassNr)
				Throw.SysError(MessageInfo.Get("Pokus o nalezení záznamu typu Task, jehož GID odkazuje na neplatnou třídu (požadavek=%0, povolená hodnota=%1).", taskGID.ClassNumber, CapacityPlanWorkItemCls.ClassNr));
			return this.FindTaskCItem(taskGID.RecordNumber);
		}
		/// <summary>
		/// Najde a vrátí záznam kapacitního úkolu podle jeho ID
		/// </summary>
		/// <param name="taskID">Klíč kapacitního úkolu</param>
		/// <returns>Data kapacitního úkolu</returns>
		public CapacityPlanWorkItemCls FindTaskCItem(int taskID)
		{
			CapacityPlanWorkItemCls result;
			if (this._TaskC.TryGetValue(taskID, out result))
				return result;
			return null;
		}
		/// <summary>
		/// Najde a vrátí soupis tasků podle daného filtru
		/// </summary>
		/// <returns></returns>
		internal List<CapacityPlanWorkItemCls> FilterTask(Predicate<CapacityPlanWorkItemCls> filter)
		{
			return this._TaskC.Values.Where(t => filter(t)).ToList();
		}
		/// <summary>
		/// Najde a vrátí záznam úkolu (Task), který je vytvořen pro daný záznam osy (příjemka výrobku) a číslo operace (její klíč, buď STPV, nebo VP).
		/// Toto určení je jednoznačné: pro jeden záznam osy S nemůže být více tasků, které by se vázaly ke stejné operaci.
		/// Bez specifikace záznamu osy S by mohlo dojít k duplicitě: operace vytvořené pro typ záznamu Plán mají klíč operace z STPV, a ta se může opakovat pro více záznamů.
		/// K jednomu záznamu osy S o příjmu z výroby se váže více operací, ale každá má své unikátní číslo operace.
		/// </summary>
		/// <param name="axisId">ID záznamu osy</param>
		/// <param name="operationId">ID operace</param>
		/// <returns></returns>
		public CapacityPlanWorkItemCls FindTaskCForAxisOper(int axisId, int operationId)
		{
			TwoInt32 axisOper = new TwoInt32(axisId, operationId);
			int taskId;
			if (!this._TaskOperIndex.TryGetValue(axisOper, out taskId)) return null;
			return this.FindTaskCItem(taskId);
		}
		/// <summary>
		/// Najde a vrátí záznam pracovního úkolu podle jeho GID
		/// </summary>
		/// <param name="workGID">Klíč jednotky práce</param>
		/// <returns>Data jednotky práce</returns>
		public WorkUnitCls FindIWorkItem(GID workGID)
		{
			if (workGID.ClassNumber != Constants.ClassNumberWork)
				Throw.SysError(MessageInfo.Get("Pokus o nalezení záznamu typu Work, jehož GID odkazuje na neplatnou třídu (požadavek=%0, povolená hodnota=%1).", workGID.ClassNumber, Constants.ClassNumberWork));
			return this.FindIWorkItem(workGID.RecordNumber);
		}
		/// <summary>
		/// Najde a vrátí záznam pracovního úkolu podle jeho ID
		/// </summary>
		/// <param name="workID">Klíč jednotky práce</param>
		/// <returns>Data jednotky práce</returns>
		public WorkUnitCls FindIWorkItem(int workID)
		{
			WorkUnitCls result;
			if (this._WorkD.TryGetValue(workID, out result))
				return result;
			return null;
		}
		/// <summary>
		/// Najde a vrátí záznam pracovního úkolu podle jeho lokátoru.
		/// Lokátor určuje relativní pozici jednotky WorkUnit v rámic jeho Tasku, Pass, Time.
		/// Vrácený WorkUnit odpovídá danému lokátoru nejlépe ze všech WorkUnit daného tasku.
		/// </summary>
		/// <param name="workLocation">Lokátor jednotky WorkUnit</param>
		/// <returns>Data jednotky práce</returns>
		internal WorkUnitCls FindIWorkItem(WorkUnitLocationCls workLocation)
		{
			WorkUnitCls work = null;
			CapacityPlanWorkItemCls task;
			if (this._TaskC.TryGetValue(workLocation.TaskKey, out task))
			{
				work = task.FindUnitItem(workLocation);
			}
			return work;
		}
		/// <summary>
		/// Vyhledá v paměti konkrétní časový úsek WorkTimeCls podle jeho GIDu.
		/// Tato metoda nemůže použít žádný index, hledá položku pomocí postupného scanování všech tasků -» pass -» time.
		/// Měla by být používána v minimální míře z důvodu časové náročnosti (systém na toto hledání není optimalizován).
		/// Metoda může vrátit null, pokud nic nenajde.
		/// </summary>
		/// <param name="timeGId"></param>
		/// <returns></returns>
		internal WorkTimeCls SearchForWorkTime(GID timeGId)
		{
			if (timeGId.ClassNumber != Constants.ClassNumberTime && timeGId.ClassNumber != PlanUnitCTimeCls.ClassNr) return null;
			foreach (CapacityPlanWorkItemCls task in this.TaskC.Values)
			{
				WorkTimeCls workTime = task.SearchForWorkTime(timeGId);
				if (workTime != null)
					return workTime;
			}
			return null;
		}
		/// <summary>
		/// Metoda vrátí grupovaný soupis tasků podle jejich příslušnosti do skupin podle hodnoty task.LinkGroup.
		/// Výstupem je Dictionary, jejíž Key = číslo LinkGroup, a kde Value = seznam jednotlivých tasků, které do této linkované grupy náležejí.
		/// Tasky, které nepatří do žádné grupy, budou uvedeny v prvku s klíčem 0 anebo nebudou uvedeny v žádném prvku (podle parametru withNoLink).
		/// </summary>
		/// <param name="withNoLink">Požadavek, aby se tasky nezařazené do žádného linku vkládaly do položky s klíčem 0: true = ano / false = ne</param>
		/// <returns></returns>
		public Dictionary<int, List<CapacityPlanWorkItemCls>> FindLinkedTaskGroup(bool withNoLink)
		{
			Dictionary<int, List<CapacityPlanWorkItemCls>> result = new Dictionary<int, List<CapacityPlanWorkItemCls>>();

			foreach (CapacityPlanWorkItemCls task in this._TaskC.Values)
			{
				int link = task.LinkGroup;
				if (link != 0 || withNoLink)
				{
					List<CapacityPlanWorkItemCls> list;
					if (!result.TryGetValue(link, out list))
					{
						list = new List<CapacityPlanWorkItemCls>();
						result.Add(link, list);
					}
					list.Add(task);
				}
			}

			return result;
		}
		#endregion
		#region ROZŠÍŘENÉ VYHLEDÁNÍ DAT NA ZÁKLADĚ DAT NA OSÁCH (SOUPISY VZTAHŮ, ZÁZNAMŮ)
		/// <summary>
		/// Najde a vrátí GID posledního tasku, z něhož je plánovaný příjem na sklad do záznamu osy receiptAxisGId.
		/// Jde o příjem finálu anebo vedlejšího produktu.
		/// Jinými slovy hledám ItemGIdPrev, který je ve vztahu PlanningItemRelationType.TaskToAxis na daný záznam ItemGIdNext.
		/// Pokud nenajde žádný task, vrací GID.Empty.
		/// Pokud najde víc než 1 vztah, vyhodí chybu.
		/// </summary>
		/// <param name="receiptAxisGId">Záznam osy S, příjem z výroby. Jeho GID.</param>
		/// <returns></returns>
		internal GID FindTaskGIdForReceipt(GID receiptAxisGId)
		{
			List<PlanningItemRelationCls> relList = this.FindRelationPrevIDData(receiptAxisGId,
						delegate(PlanningItemRelationCls relItem)
						{ return (relItem.Relation == PlanningItemRelationType.TaskToAxisFinal || relItem.Relation == PlanningItemRelationType.TaskToAxisByProductSuitable); });
			if (relList.Count > 1)
				Throw.SysError(MessageInfo.Get("Chyba plánovacího procesu: pro jednu příjemku na sklad %0 eviduje více vztahů na příjem z výroby (počet = %1).", receiptAxisGId, relList.Count));
			else if (relList.Count == 1)
				return relList[0].ItemGIDPrev;
			return GID.Empty;
		}
		/// <summary>
		/// Metoda najde a vrátí seznam operací výroby jednoho dílce, počínaje danou operací v daném směru.
		/// Zadaná operace bude první ve výsledném seznamu.
		/// Pokud bude zadán GID prázdný anebo jiné třídy než Constants.ClassNumberTask, bude vrácen prázdný list (bez zadaného GIDu).
		/// Současně je metoda ochotná vygenerovat seznam vztahů o výdeji do komponent těchto operací.
		/// </summary>
		/// <param name="taskGId">GID první operace, která bude vrácená, a od níž se začne hledat výrobní linie</param>
		/// <param name="timeDir">Směr hledání výrobních operací</param>
		/// <returns></returns>
		internal List<GID> FindTaskGIdInnerList(GID taskGId, TimeRange.TimeDirection timeDir)
		{
			List<PlanningItemRelationCls> componentList = null;
			return this.FindTaskGIdInnerList(taskGId, timeDir, ref componentList);
		}
		/// <summary>
		/// Metoda najde a vrátí seznam operací výroby jednoho dílce, počínaje danou operací v daném směru.
		/// Zadaná operace bude první ve výsledném seznamu.
		/// Pokud bude zadán GID prázdný anebo jiné třídy než Constants.ClassNumberTask, bude vrácen prázdný list (bez zadaného GIDu).
		/// Současně je metoda ochotná vygenerovat seznam vztahů o výdeji do komponent těchto operací.
		/// </summary>
		/// <param name="taskGId">GID první operace, která bude vrácená, a od níž se začne hledat výrobní linie</param>
		/// <param name="timeDir">Směr hledání výrobních operací</param>
		/// <param name="componentList">Soupis kompoment vstupujících do operací, které se vrací</param>
		/// <returns></returns>
		internal List<GID> FindTaskGIdInnerList(GID taskGId, TimeRange.TimeDirection timeDir, ref List<PlanningItemRelationCls> componentList)
		{
			List<GID> result = new List<GID>();

			// Kontrola zadání:
			if (taskGId.IsEmpty || taskGId.ClassNumber != Constants.ClassNumberTask) return result;
			if (!(timeDir == TimeRange.TimeDirection.ToFuture || timeDir == TimeRange.TimeDirection.ToHistory)) return result;
			bool withComponents = (componentList != null);
			// bool toHistory = (timeDir == TimeRange.TimeDirection.ToHistory);

			// Zásobník operací (TaskInner), které budu postupně procházet, dohledávat, doplňovat a procházet:
			Stack<GID> taskStack = new Stack<GID>();
			taskStack.Push(taskGId);

			// Antiduplicita:
			Hashtable scanGIds = new Hashtable();
			while (taskStack.Count > 0)
			{
				// Jdeme operaci za operací do minulosti, zvládáme i boční Inner operace (co kdyby?):
				GID currGid = taskStack.Pop();
				if (currGid.IsEmpty) continue;
				if (scanGIds.ContainsKey(currGid)) continue;
				scanGIds.Add(currGid, null);

				// Operaci (její GID) do výstupu:
				result.Add(currGid);

				// a) komponenty:
                if (withComponents)
                    componentList.AddRange(this.FindRelationPrevIDData(currGid, r => r.Relation == PlanningItemRelationType.AxisToTask));

				// b) operace předchozí / následující ve směru timeDir, počínaje záznamem currGid, pouze vztahy TaskInner:
				List<PlanningItemRelationCls> taskList = this.FindRelationOtherIDData(timeDir, currGid, r=> (r.Relation == PlanningItemRelationType.TaskInner));
				foreach (PlanningItemRelationCls taskItem in taskList)
					taskStack.Push(taskItem.ItemGIDEdge(timeDir));
			}
			return result;
		}
		#endregion
		#region ZÁVĚREČNÉ MAPOVÁNÍ VZTAHŮ PO DOKONČENÍ PLÁNOVACÍHO PROCESU
		/// <summary>
		/// Mapuje vztahy mezi záznamy: na základě vztahů na nízké úrovni, které generuje plánovací proces, 
		/// dohledá vztahy vyšší úrovně (mezi záznamy osy, finální výdeje, atd).
		/// </summary>
		internal void MapFinalRelations()
		{
			// Kontrola: mapování lze provést jen jednou po dokončení plánování !!!
			if (FinalMapCreated)
				Throw.SysError(MessageInfo.Get("Byl detekován pokus o opakované generování mapy finálních vztahů. To nejde."));

			this._MapFinalRelationsTransferTask();

			this._MapFinalRelationsAxisOutput(false);
		}
		/// <summary>
		/// Metoda najde transfery mezi operacemi (sekundární vztah PlanningItemRelationType.TaskTransfer)
		/// </summary>
		private void _MapFinalRelationsTransferTask()
		{
			// Najdu vztahy mezi tasky v různých dílcích (tzn. cestu polotovaru z jeho příjemky, přes sklady do jiné výroby jako komponenta):

			// Najdu všechny vztahy o příjmu z výroby na osu (finální operace => výrobek, kterákoli operace =>vedlejší produkt):
			List<PlanningItemRelationCls> relList = this.FindRelationTypesData(PlanningItemRelationType.TaskToAxisFinal, PlanningItemRelationType.TaskToAxisByProductSuitable);
			Dictionary<int, Hashtable> tttDict = new Dictionary<int, Hashtable>();      // Antiduplicitní tabulka: pro každý task (koncový) je zde seznam tasků, do nichž jsme už našli vztah (aby se nepřidával podruhé)
			Hashtable scanGIds = new Hashtable();
			for (int r = 0; r < relList.Count; r++)       // Pozor: Count se postupně navyšuje!
			{
				// Vztah z posledního tasku (operace) jedné výroby na osu: 
				//   (je dáno výběrem dle indexu Type = PlanningItemRelationType.TaskToAxis)
				PlanningItemRelationCls relItem = relList[r];
				GID axisGId = relItem.ItemGIDNext;

				// Pokud jsme pro tento příjem na osu S už vztahy našli, přeskočíme ji:
				if (scanGIds.ContainsKey(axisGId)) continue;
				scanGIds.Add(axisGId, null);

				// Najdeme všechny další tasky, do nichž se vydává polotovar, který je přijatý na osu S číslo axisGId (záznamem o vztahu relItem):
				List<GID> nextTaskList = this.FindTargetNextTaskGIdList(axisGId);

				// Vytvoříme (jen poprvé !!!) vztah mezi posledním taskem předešlého dílce (máme jeho příjem na osu S v relItem)
				//   a taskem ve vyšším dílci, kam přes osu S tento polotovar doputoval (task má GID v seznamu nextTaskList):
				// Současně dohledáme poslední task v cílové řadě operací, abychom mohli spustit další kolo hledání vztahu TaskTransfer:
				GID prevTask = relItem.ItemGIDPrev;
				int prevID = prevTask.RecordNumber;
				if (!tttDict.ContainsKey(prevID))
					tttDict.Add(prevID, new Hashtable());        // Soupis čísel tasků, které jsme již navázali z tasku prevTask

				// 
				foreach (GID nextTask in nextTaskList)
				{
					// Zjistíme, zda vztah z tasku (prevTask) do tasku (nextTask) vidíme poprvé:
					if (!tttDict[prevID].ContainsKey(nextTask.RecordNumber))
					{
						// Číslo Next tasku do soupisu tasků, které jsme již navázali z výchozího tasku (prevID):
						tttDict[prevID].Add(nextTask.RecordNumber, null);

						// Tady přidávám vztah z finálního tasku nižšího dílce (prevTask) na další task vyššího dílce (nextTask):
						PlanningItemRelationCls rItem = new PlanningItemRelationCls(PlanningItemRelationType.TaskTransfer, prevTask, nextTask, 0M);
						this.AddItemRelation(rItem);

						// Máme dohledat i další vyšší cestu výrobku z nextTasku (do jakých dalších tasků ten vyšší dílec šel):
						if (!scanGIds.ContainsKey(nextTask))
						{	// Jen tehdy, když task (nextTask) jsme dosud neprocházeli (stejně bychom došli k opakovaným výsledkům):
							scanGIds.Add(nextTask, null);
							// Najdu vztah o převodu výsledného dílce z posledního tasku výroby na osu S, a tento vztah přidám k dalšímu zkoumání:
							List<PlanningItemRelationCls> nextFinalRels = this.FindTargetRelListTaskToAxis(nextTask, true);
							foreach (PlanningItemRelationCls nextRel in nextFinalRels)
								relList.Add(nextRel);
						}
					}
				}
			}
		}
		/// <summary>
		/// Metoda vytvoří veškeré vztahy typu PlanningItemRelationType.AxisOutput pro všechny patřičné záznamy na ose S.
		/// Tuto metodu je třeba volat po dokončení plánování i po dokončení načítání dat z databáze.
		/// Žádná jiná metoda nesmí zakládat vztahy typu AxisOutput.
		/// Tyto vztahy slouží jako základna pro čtení dat z osy S do grafu typu Gantt.
		/// Volitelně je možno nastavit požadavek (withRemoveOld = true), aby před tím byly odstraněny veškeré vztahy typu AxisOutput.
		/// Má se za to, že v korektním programu je to zbytečné, protože tyto vztahy generuje pouze tato metoda.
		/// </summary>
		/// <param name="withRemoveOld"></param>
		private void _MapFinalRelationsAxisOutput(bool withRemoveOld)
		{
			if (withRemoveOld)
			{	// Najdu všechny současné vztahy typu AxisOutput, a odeberu je:
				// Vypadá to snadné, ale není to příliš rychlá operace (trvá několik milisekund)
				List<PlanningItemRelationCls> relList = this.FindRelationTypeData(PlanningItemRelationType.AxisOutput);
				this.RemoveItemRelationForRelID(relList);
			}

			foreach (MaterialPlanAxisItemCls axisItem in this._AxisS.Values)
			{	// Pokud tento záznam patří do grafu Gantt Axis S (do Root úrovně), přidat vztah o výstupu:
				// Identický záznam se zakládá v procesu plánování, viz PlanningDataCls._FillAxisFromWhDocs()
				if (axisItem.IsForGraphAxisS)
					this.AddItemRelation(new PlanningItemRelationCls(PlanningItemRelationType.AxisOutput, axisItem.AxisGID, new GID(Constants.ClassNumberOutput, 0), axisItem.Qty));
			}
		}
		#region DOHLEDÁNÍ VZTAHU MEZI OSOU S A KAPACITNÍM ÚKOLEM
		/// <summary>
		/// Metoda najde a vrátí soupis všech tasků, do nichž vstupuje záznam osy S jako komponenta.
		/// Metoda hledá přes všechny převody v rámci skladu / meziskladové převody.
		/// <para>
		/// Pokud tedy např. máme záznam osy o příjmu 200 ks, a z tohoto množství zajistíme výrobu pro čtyři výrobní příkazy
		/// (např. s množstvím 20, 30, 40, 60 ks, plus zbyde na skladě 50 ks), pak tato metoda vrátí GIDy těch tasků, 
		/// do nichž vstupuje zde uvedený materiál (vrátí GIDy operací).
		/// </para>
		/// </summary>
		/// <param name="axisGId">GID výchozího záznamu osy S</param>
		/// <returns>Seznam tasků, do nichž vstupuje materiál/polotovar z daného záznamu osy S</returns>
		internal List<GID> FindTargetNextTaskGIdList(GID axisGId)
		{
			List<PlanningItemRelationCls> relList = this.FindTargetNextTaskRelList(axisGId);
			List<GID> result = new List<GID>();
			foreach (PlanningItemRelationCls rel in relList)
				result.Add(rel.ItemGIDNext);
			return result;
		}
		/// <summary>
		/// Metoda najde a vrátí soupis všech vztahů o výdeji z osy S do tasku (AxisToTask) pro vstupní záznam osy S, ve směru Next (doprava).
		/// Vstupem je libovolný záznam osy S, metoda hledá vztahy Next, 
		/// pokud najde vztahy typu balancování na ose nebo převod (AxisBalance nebo AxisTransfer), prohledává rekurzivně i navázané prvky next
		/// Pokud najde vztah typu AxisToTask, pak jej vloží do výstupu.
		/// Metoda najde všechny převody daného dílce / materiálu do výroby jako komponenta. Přes všechny vnitroskladové převody.
		/// </summary>
		/// <param name="axisGId">GID záznamu osy S</param>
		/// <returns>Soupis vztahů typu AxisToTask, kde záznam osy S nějakou cestou pochází z daného GIDu</returns>
		internal List<PlanningItemRelationCls> FindTargetNextTaskRelList(GID axisGId)
		{
			List<PlanningItemRelationCls> result = new List<PlanningItemRelationCls>();     // Výsledek
			Queue<GID> workQueue = new Queue<GID>();        // Fronta práce
			Hashtable scanHt = new Hashtable();             // Položky, které jsem už jednou našel, nebudu hledat podruhé

			workQueue.Enqueue(axisGId);                     // První úkol
			while (workQueue.Count > 0)
			{
				GID prevGid = workQueue.Dequeue();
				if (scanHt.ContainsKey(prevGid)) continue;
				scanHt.Add(prevGid, null);

				// Vztahy Next (doprava):
				List<PlanningItemRelationCls> nextRels = this.FindRelationNextIDData(prevGid);
				foreach (PlanningItemRelationCls nextRel in nextRels)
				{
					GID nextGId = nextRel.ItemGIDNext;
					if (nextGId.RecordNumber == 0) continue;

					switch (nextRel.Relation)
					{
						case PlanningItemRelationType.AxisBalance:
						case PlanningItemRelationType.AxisTransfer:
							// Převody přes osu S uložím do fronty práce, vrátím se k nim v některém příštím cyklu:
							workQueue.Enqueue(nextGId);
							break;
						case PlanningItemRelationType.AxisToTask:
							// Hledaný záznam (AxisToTask) dám do resultu:
							result.Add(nextRel);
							break;
					}
				}
			}

			return result;
		}
		/// <summary>
		/// Metoda najde a vrátí soupis všech vztahů o výdeji z osy S do tasku (AxisToTask) pro vstupní záznam osy S, ve směru Next (doprava).
		/// Vstupem je libovolný záznam osy S, metoda hledá vztahy Next, 
		/// pokud najde vztahy typu balancování na ose nebo převod (AxisBalance nebo AxisTransfer), prohledává rekurzivně i navázané prvky next
		/// Pokud najde vztah typu AxisToTask, pak jej vloží do výstupu.
		/// Metoda najde všechny převody daného dílce / materiálu do výroby jako komponenta. Přes všechny vnitroskladové převody.
		/// Ve výstupu jsou klíče cílových tasků (GId) = operace, kam se komponenta vydává, 
		/// dále pak zpoždění na skladové cestě (Delay)
		/// a množství komponenty, které do dané operace vstupuje z výchozího záznamu osy S.
		/// </summary>
		/// <param name="axisGId">GID záznamu osy S, z něhož se začíná hledat (doprava)</param>
		/// <returns>Soupis dat o operacích (kapacitní úkol) + zpoždění + množství, do nichž se vydává položka z dané osy S.</returns>
		internal List<PlanningTransferCls> FindTargetTransferToTaskList(GID axisGId)
		{
			List<PlanningTransferCls> result = new List<PlanningTransferCls>();     // Výsledek

			// Zásobník práce Queue<> bude obsahovat položky PlanningTransferCls:
			//  jednak výchozí GID, pro který se hledá další vztah doprava,
			//  a jednak sumární čas (Delay) a dále množství, odpovídající množství tohoto záznamu pocházející z prvotního množství:
			// Množství se nepočítá pomocí nějakých poměrových koeficientů, ale prostě se snižuje.
			// Pokud z výchozího záznamu (příjem na osu) například 100ks mám výdeje {40,40,20} na další sklad, pak mám tři záznamy {40,40,20}.
			// Z nich následně najdu (přes vztah AxisTransfer) záznamy na ose na dalším skladě 
			//  (transfery by měly být 1:1, ale cílem transferů mohou být různé sklady).
			// Na tomto skladě se příjmy mohou kumulovat a následně někam vydávat, o tom jsou vztahy typu AxisBalance, typicky výdej do komponenty VP.
			//  Pokud například z těch 40ks vydám jako komponentu do více VP, budu mít mezi příjmem (40) a výdaji vztah AxisBalance s patřičným množstvím.
			//  Tak mohu těch 40ks rozdělit do komponent do vztahů s možstvím { 15,15,10 }...
			Queue<PlanningTransferCls> workQueue = new Queue<PlanningTransferCls>();
			Hashtable scanHt = new Hashtable();             // Položky, které jsem už jednou našel, nebudu hledat podruhé

			// První úkol:
			MaterialPlanAxisItemCls axis = this.FindAxisSItem(axisGId);
			if (axis != null)
				workQueue.Enqueue(new PlanningTransferCls(axisGId, TimeSpan.Zero, axis.Qty));

			while (workQueue.Count > 0)
			{	// Co budeme dělat v tomto kroku:
				PlanningTransferCls prevItem = workQueue.Dequeue();
				if (scanHt.ContainsKey(prevItem.GId)) continue;
				scanHt.Add(prevItem.GId, null);

				// Vztahy Next (doprava):
				List<PlanningItemRelationCls> nextRels = this.FindRelationNextIDData(prevItem.GId);
				foreach (PlanningItemRelationCls nextRel in nextRels)
				{
					GID nextGId = nextRel.ItemGIDNext;
					if (nextGId.RecordNumber == 0) continue;

					switch (nextRel.Relation)
					{
						case PlanningItemRelationType.AxisBalance:
						case PlanningItemRelationType.AxisTransfer:
							// Vztahy o převodu mezi záznamy jedné osy (AxisBalance) a o převodu mezi sklady (AxisTransfer), vše mezi záznamy osy S:
							// Převody přes osu S uložím do fronty práce, vrátím se k nim v některém příštím cyklu:
							MaterialPlanAxisItemCls nextAxis = this.FindAxisSItem(nextRel.ItemGIDNext);
							if (nextAxis != null)
							{
                                // Zpoždění na "předávací lince" počítám z rozdílu časů End - Begin na ose nextAxis:
                                // (Tady byla chyba nalezená na Brano při převodech mezi podniky, kde byla doba dodávky + rezerva = 43 dnů, a počítala se 2x).
                                TimeSpan nextDelay = (nextAxis.IsIncrement ? TimeSpan.FromDays((double)(nextAxis.DeliveryTime + nextAxis.DeliveryTimeRes)) : TimeSpan.Zero);
                                nextDelay = (nextAxis.DatePlanEnd - nextAxis.DatePlanBegin);
                                if (nextDelay.TotalDays < 0d)
                                    nextDelay = TimeSpan.Zero;

                                TimeSpan sumDelay = prevItem.Delay + nextDelay;
								decimal nextQty = Math.Min(prevItem.Qty, nextRel.LinkedQty);
								workQueue.Enqueue(new PlanningTransferCls(nextRel.ItemGIDNext, sumDelay, nextQty));
							}
							break;
						case PlanningItemRelationType.AxisToTask:
							// Vztah o převodu z osy S do tasku C:
							// Hledaný záznam (AxisToTask) dám do resultu:
							result.Add(new PlanningTransferCls(nextRel, nextRel.ItemGIDNext, prevItem.Delay, prevItem.Qty));
							break;
					}
				}
			}

			return result;
		}
		/// <summary>
		/// Metoda najde a vrátí soupis všech tasků, které jsou zdrojem finálního výrobku (poslední operace výroby),
		/// které zásobují daný záznam osy S.
		/// Metoda hledá přes všechny převody v rámci skladu / meziskladové převody.
		/// <para>
		/// Pokud tedy např. máme záznam osy o výdeji 120 ks, z čehož 20 ks je pokryto skladovou zásobou, 
		/// a na zbývajících 100 ks jsou vystaveny výrobní příkazy po 50 ks, vrátí seznam 2 GIDů, 
		/// které ukazují na poslední operace výroby každých 50 ks.
		/// </para>
		/// </summary>
		/// <param name="axisGId">GID záznamu osy S, z něhož vycházíme (doleva)</param>
		/// <param name="withByProducts">Do výstupu zahrnout i vedlejší produkty: true = ano / false = ne</param>
		/// <returns>Seznam GIDů tasků, které jsou poslední operací výroby, z níž pochází zajištění daného záznamu osy S</returns>
		internal List<GID> FindTargetPrevTaskGIdList(GID axisGId, bool withByProducts)
		{
			List<PlanningItemRelationCls> relList = FindTargetPrevTaskRelList(axisGId, withByProducts);
			List<GID> result = new List<GID>();
			foreach (PlanningItemRelationCls rel in relList)
				result.Add(rel.ItemGIDPrev);
			return result;
		}
		/// <summary>
		/// Metoda najde a vrátí soupis všech vztahů o příjmu z výrobní operace na osu S (TaskToAxis) pro vstupní záznam osy S, ve směru Prev (doleva).
		/// Vstupem je libovolný záznam osy S, metoda hledá vztahy Prev.
		/// Pokud najde vztahy typu balancování na ose nebo převod (AxisBalance nebo AxisTransfer), prohledává rekurzivně i navázané prvky Prev.
		/// Pokud najde vztah typu TaskToAxis, pak jej vloží do výstupu.
		/// Metoda najde všechny příjmy daného dílce z výroby jako finály. Přes všechny vnitroskladové převody.
		/// </summary>
		/// <param name="axisGId">GID záznamu osy S, z něhož vycházíme (doleva)</param>
		/// <param name="withByProducts">Do výstupu zahrnout i vedlejší produkty: true = ano / false = ne</param>
		/// <returns>Soupis vztahů typu TaskToAxis, kde záznam osy S nějakou cestou vede k danému GIDu</returns>
		internal List<PlanningItemRelationCls> FindTargetPrevTaskRelList(GID axisGId, bool withByProducts)
		{
			List<PlanningItemRelationCls> result = new List<PlanningItemRelationCls>();     // Výsledek
			Queue<GID> workQueue = new Queue<GID>();        // Fronta práce
			Hashtable scanHt = new Hashtable();             // Položky, které jsem už jednou našel, nebudu hledat podruhé

			workQueue.Enqueue(axisGId);                     // První úkol
			while (workQueue.Count > 0)
			{
				GID nextGid = workQueue.Dequeue();
				if (scanHt.ContainsKey(nextGid)) continue;
				scanHt.Add(nextGid, null);

				// Vztahy Prev (doleva):
				List<PlanningItemRelationCls> prevRels = this.FindRelationPrevIDData(nextGid);
				foreach (PlanningItemRelationCls prevRel in prevRels)
				{
					GID prevGId = prevRel.ItemGIDPrev;
					if (prevGId.RecordNumber == 0) continue;

					switch (prevRel.Relation)
					{
						case PlanningItemRelationType.AxisBalance:
						case PlanningItemRelationType.AxisTransfer:
							workQueue.Enqueue(prevGId);
							break;
						case PlanningItemRelationType.TaskToAxisFinal:
							// Příjem z výroby vždy:
							result.Add(prevRel);
							break;
						case PlanningItemRelationType.TaskToAxisByProductSuitable:
							// Vedlejší produkty jen pokud jsou žádány:
							if (withByProducts)
								result.Add(prevRel);
							break;
					}
				}
			}

			return result;
		}
		#endregion
		#region DOHLEDÁNÍ PRVNÍHO / POSLEDNÍHO TASKU V PŘÍMÉ VÝROBNÍ ŘADĚ (najde první / poslední operaci jedné výroby)
		/// <summary>
		/// Metoda najde a vrátí GID prvního tasku výroby, přičemž vstupní GID může být libovolný task v této výrobě.
		/// </summary>
		/// <param name="taskGId">GID tasku, jehož první operaci hledáme.</param>
		internal GID FindTargetFirstTask(GID taskGId)
		{
			return FindTargetEdgeTask(taskGId, TimeRange.TimeDirection.ToHistory, false);
		}
		/// <summary>
		/// Metoda najde a vrátí GID posledního tasku výroby, přičemž vstupní GID může být libovolný task v této výrobě.
		/// </summary>
		/// <param name="taskGId">GID tasku, jehož poslední operaci hledáme.</param>
		internal GID FindTargetLastTask(GID taskGId)
		{
			return FindTargetEdgeTask(taskGId, TimeRange.TimeDirection.ToFuture, false);
		}
		/// <summary>
		/// Metoda najde a vrátí GID prvního/posledního tasku výroby, přičemž vstupní GID může být libovolný task v této výrobě.
		/// Pro směr ToFuture vrátí poslední operaci výroby, pro ToHistory vrátí první operaci.
		/// Při nastavení parametru (reverse) na true vrací opačně.
		/// </summary>
		/// <param name="taskGId">GID tasku, jehož krajní hledáme.</param>
		/// <param name="timeDir">Směr hledání sousedních položek: ToFuture vrací položky Next, ToHistory vrací položky Prev. Jiný směr vrací null.</param>
		/// <param name="reverse">Obraceč směru času. Pokud je true, pak pro směr ToFuture vrací Prev a pro směr ToHistory vrací položky Next.</param>
		internal GID FindTargetEdgeTask(GID taskGId, TimeRange.TimeDirection timeDir, bool reverse)
		{
			GID result = taskGId;
			Hashtable scanHt = new Hashtable();             // Položky, které jsem už jednou našel, nebudu hledat podruhé (proti zacyklení!)
			while (true)
			{
				// Najdu seznam vztahů, které představují vztah na předešlou/následující operaci téže výrobní linky (TaskInner):
				List<PlanningItemRelationCls> nextRels = this.FindRelationOtherIDData(timeDir, reverse, result, 
					delegate(PlanningItemRelationCls r) { return (r.Relation == PlanningItemRelationType.TaskInner); });
				if (nextRels.Count == 0) break;

				// Načtu GID strany Next/Prev = následující operace:
				GID nextGId = nextRels[0].ItemGIDEdge(timeDir, reverse);
				if (scanHt.ContainsKey(nextGId)) break;
				scanHt.Add(nextGId, null);
				result = nextGId;
			}
			return result;
		}

		/// <summary>
		/// Metoda najde a vrátí soupis záznamů o vztahu TaskToAxis ze všech operací jedné výrobní linky,
		/// dané kterýmkoli jejímn taskem (operací).
		/// Každá výroba může obsahovat 1 vztah o převodu na osu TaskToAxisFinal,
		/// a Nn dalších příjmů na osu S pro vedlejší produkty.
		/// </summary>
		/// <param name="taskGId">GID tasku, jehož převod TaskToAxis hledáme.</param>
		/// <param name="withByProducts">Do výstupu zahrnout i vedlejší produkty: true = ano / false = ne</param>
		internal List<PlanningItemRelationCls> FindTargetRelListTaskToAxis(GID taskGId, bool withByProducts)
		{
			List<PlanningItemRelationCls> result = new List<PlanningItemRelationCls>();
			GID currentGId = taskGId;
			Hashtable scanHt = new Hashtable();             // Položky, které jsem už jednou našel, nebudu hledat podruhé (proti zacyklení!)
			while (true)
			{
				if (currentGId.IsEmpty) break;
				if (scanHt.ContainsKey(currentGId)) break;
				scanHt.Add(currentGId, null);

				// Najdu seznam vztahů doprava (další operace, příjem na osu, atd):
				List<PlanningItemRelationCls> nextRels = this.FindRelationNextIDData(currentGId);
				if (nextRels.Count == 0) break;

				// Zajímá nás příjem na osu (finál, vedlejší produkt) anebo další operace (tu projdeme rekurzivně):
				currentGId = GID.Empty;
				foreach (PlanningItemRelationCls rel in nextRels)
				{
					switch (rel.Relation)
					{
						case PlanningItemRelationType.TaskToAxisFinal:
							// Pokud jde o vztah o převodu finálního výrobku z tasku na osu, přidám jej do výsledku bezpodímenčně:
							result.Add(rel);
							break;
						case PlanningItemRelationType.TaskToAxisByProductSuitable:
							// Vedlejší produkty jen pokud jsou žádány:
							if (withByProducts)
								result.Add(rel);
							break;
						case PlanningItemRelationType.TaskInner:
							// Následující operace: projdu ji v dalším kroku (víc než jedna nebude, výroba je zatím lineární):
							currentGId = rel.ItemGIDNext;
							break;
					}
				}
			}
			return result;
		}
		#endregion
		#endregion
        #region VYHLEDÁNÍ TEXTU GetReferNazev()
        /// <summary>
        /// Vrátí Refer: Nazev pro daný záznam.
        /// Pokud neexistuje, vrátí "".
        /// </summary>
        /// <param name="recordGId"></param>
        /// <returns></returns>
        public bool ContainReferNazev(int classNumber)
        {
            System.Collections.IDictionary dict = this._GetDictionaryForClass(classNumber);
            return (dict != null);
        }
        /// <summary>
        /// Vrátí Refer: Nazev pro daný záznam.
        /// Pokud neexistuje, vrátí "".
        /// </summary>
        /// <param name="recordGId"></param>
        /// <returns></returns>
        public string GetReferNazev(GID recordGId)
        {
            return this.GetReferNazev(recordGId.ClassNumber, recordGId.RecordNumber);
        }
        /// <summary>
        /// Vrátí Refer: Nazev pro daný záznam.
        /// Pokud neexistuje, vrátí "".
        /// </summary>
        /// <param name="recordGId"></param>
        /// <returns></returns>
        public string GetReferNazev(int classNumber, int recordNumber)
        {
            System.Collections.IDictionary dict = this._GetDictionaryForClass(classNumber);
            if (dict == null) return "";
            if (!dict.Contains(recordNumber)) return "";
            object value = dict[recordNumber];
            string result = "";
            IReferNazev record = value as IReferNazev;
            if (record != null)
                result = record.ReferNazev;
            if (record is MaterialPlanAxisItemCls)
                result += " = " + this.ParentPlanning.GetReferNazev(PlanUnitSCls.ClassNr, ((MaterialPlanAxisItemCls)record).PlanUnitS);
            else if (record is CapacityPlanWorkItemCls)
            {
                CapacityPlanWorkItemCls task = record as CapacityPlanWorkItemCls;
                switch (task.DocumentType)
                {
                    case TDocumentType.Production:
                        result += " = " + this.ParentPlanning.GetReferNazev(ProductOrderOperationCls.ClassNr, task.OperationKey);
                        break;
                    case TDocumentType.PlanApproved:
                        result += " = " + this.ParentPlanning.GetReferNazev(AOperationCls.ClassNr, task.OperationKey);
                        break;
                }
            }
            return result;
        }
        /// <summary>
        /// Najde a vrátí Dictionary pro dané číslo třídy
        /// </summary>
        /// <param name="classNumber"></param>
        /// <returns></returns>
        private System.Collections.IDictionary _GetDictionaryForClass(int classNumber)
        {
            switch (classNumber)
            {
                case MaterialPlanAxisItemCls.ClassNr:
                case PlanUnitSAxisCls.ClassNr:
                    return this._AxisS;
                case CapacityPlanWorkItemCls.ClassNr:
                case PlanUnitCTaskCls.ClassNr:
                    return this._TaskC;
            }
            return null;
        }
        #endregion
    }
	#endregion
	#region CLASS MaterialDataStatusCls : sbírka stavových proměnných modulu MaterialData
	/// <summary>
	/// AxisHeapStatusCls : sbírka stavových proměnných modulu AxisHeap
	/// </summary>
	internal class AxisHeapStatusCls
	{
		/// <summary>
		/// Příznak, zda již byla vygenerována finální mapa vztahů.
		/// </summary>
		internal bool FinalMapCreated { get; set; }
		/// <summary>
		/// Obsahuje příznak, že aktuální data obsahují změny, které je třeba uložit do databáze.
		/// </summary>
		public bool ContainDirtyData { get; set; }
	}
	#endregion
	#region CLASS MaterialPlanAxisItemCls : Jedna položka změny stavu materiálu na plánovací ose S.
	/// <summary>
	/// Jedna položka změny stavu materiálu na plánovací ose S.
	/// </summary>
	public class MaterialPlanAxisItemCls : PlanItemCls<object>, IRecordNumber, IReferNazev
	{
		#region KONSTRUKCE - ZÁKLADNÍ PROMĚNNÉ
		/// <summary>
		/// Privátní konstruktor. Instanci této třídy je nutno vytvořit statickým konstruktorem s naplněním daty.
		/// </summary>
		private MaterialPlanAxisItemCls() 
			: base(null)
		{
			_PlanningState = PlanSAxisPlanningState.Active;
			_ScheduledState = PlanSAxisSchedulingState.None;
		}
		/// <summary>{ get; } ID této položky osy, klíč platný po dobu existence v paměti (nejde o číslo nonsubjektu). Vždy se automaticky vygeneruje další kladné číslo.</summary>
		public int AxisID { get { return this.ItemID; } }
		/// <summary> { get; } Unikátní GID osy = číslo třídy plus můj primární klíč</summary>
		public GID AxisGID { get { return new GID(ClassNr, this.ItemID); } }
		/// <summary> { get; } Unikátní GID osy pro grafy, kde se tento řádek vyskytuje v podřízeném postavení = číslo třídy ClassNumberAxisSubRow (0x10009) plus můj primární klíč</summary>
		public GID AxisGIDSubRow { get { return new GID(Constants.ClassNumberAxisSubRow, this.ItemID); } }
		/// <summary>Číslo třídy MaterialPlanAxisItemCls</summary>
		public const int ClassNr = Constants.ClassNumberAxis;
		/// <summary>{ get; set; } Stav této věty v procesu plánování, z hlediska ID této položky osy.</summary>
		public PlanSAxisPlanningState PlanningState { get { return _PlanningState; } internal set { _PlanningState = value; } }
		private PlanSAxisPlanningState _PlanningState;
		/// <summary>{ get; } Příznak, že tento záznam pochází přímo ze skladového dokladu (příjem či výdej)</summary>
        public bool IsFromWhDoc { get { return PlanningComponentCls.AxisChangeTypeIsWarehouseDoc(this.ChangeType); } }
		/// <summary>{ get; } Příznak, že tento záznam pochází přímo ze skladového výdeje do reálné poptávky (nikoli obchodní plán ani příjem z objednávky)</summary>
        public bool IsFromWhRealEnquiry { get { return PlanningComponentCls.AxisChangeTypeIsFromRealWhEnquiry(this.ChangeType); } }
        /// <summary>{ get; } Příznak, že tento záznam pochází přímo ze skladového výdeje do libovolné poptávky plán, realita : ChangeType je DecrementByPlanEnquiry || DecrementByRealEnquiry) (nikoli příjem z objednávky)</summary>
        public bool IsFromWhAnyEnquiry { get { return PlanningComponentCls.AxisChangeTypeIsFromAnyWhEnquiry(this.ChangeType); } }
		/// <summary>{ get; } 
        /// Příznak, že tato položka osy S se má dostat do Gantt grafu (do Root úrovně) v plánovací tabuli.
		/// Do grafu patří A: poptávky (reálné i poptávky obchodního plánu),
		/// a rovněž B: příjmy z výrobních příkazů (existující, plánované, objednané), 
		/// pokud z jejich množství něco zůstane na skladu.
		/// </summary>
		public bool IsForGraphAxisS { get { return this._GetIsForGraphAxisS(); } }
		/// <summary>{ get; } Příznak, že tento záznam pochází z nějakého reálného podkladu (objednávka, poptávka, výrobní příkaz) a ne z návrhu.</summary>
		public bool IsFromOriginalRecord { get { return PlanningComponentCls.AxisChangeTypeIsFromOriginalRecord(this.ChangeType); } }
		/// <summary>{ get; } Příznak, že tento záznam pochází z výrobního příkazu (nikoli z návrhu na výrobní příkaz)</summary>
		public bool IsFromProductOrder { get { return (this.ChangeType == PlanSAxisChangeType.IncrementByRealProductOrder); } }
        /// <summary>{ get; } Příznak, že tento záznam pochází z komponenty výrobního příkazu (nikoli z návrhu na výdej do kompomnenty)</summary>
        public bool IsFromProductOrderStructure { get { return (this.ChangeType == PlanSAxisChangeType.DecrementByRealComponent); } }
        /// <summary>{ get; } Příznak, že tento záznam je příjmem finálního výrobku z výroby (výrobní příkaz, návrh, objednávka) (IncrementByOrderProduction, IncrementByRealProductOrder, IncrementByPlanProductOrder)</summary>
		public bool IsReceiptFinalProduct { get { return PlanningComponentCls.AxisChangeTypeIsReceiptFinalProduction(this.ChangeType); } }
		/// <summary>{ get; } Příznak, že tento záznam je příjmem vedlejšího výrobku z výroby (výrobní příkaz, návrh)</summary>
		public bool IsReceiptByProduct { get { return PlanningComponentCls.AxisChangeTypeIsReceiptByProduction(this.ChangeType); } }
		/// <summary>{ get; } Příznak, že tento záznam je typu Příjem (reálný / návrh), bez ohledu na aktuální množství</summary>
		public bool IsIncrement { get { return PlanningComponentCls.AxisChangeTypeIsIncrement(this.ChangeType); } }
		/// <summary>{ get; } Příznak, že tento záznam je typu Výdej (reálný / návrh), bez ohledu na aktuální množství</summary>
		public bool IsDecrement { get { return PlanningComponentCls.AxisChangeTypeIsDecrement(this.ChangeType); } }
		/// <summary>
		/// Zjistí, zda mezi důvody vzniku tohoto záznamu je některý z dotazovaných důvodů.
		/// (jde o enum, který je Flags, hodnoty lze bitově sčítat).
		/// </summary>
		/// <param name="originReason"></param>
		/// <returns></returns>
		public bool IsForOriginReason(PlanSAxisOriginReason originReason)
		{
			return ((this._OriginReason & originReason) > 0);
		}
        /// <summary>
        /// Zjistí, zda tento záznam osy je některého z vyjmenovaných typů
        /// </summary>
        /// <param name="changeTypes"></param>
        /// <returns></returns>
        public bool IsAnyOfChangeType(params PlanSAxisChangeType[] changeTypes)
        {
            if (changeTypes == null || changeTypes.Length == 0) return false;
            PlanSAxisChangeType changeType = this.ChangeType;
            foreach (PlanSAxisChangeType ct in changeTypes)
            {
                if (ct == changeType) return true;
            }
            return false;
        }
        /// <summary>
        /// Obsahuje číslici 1-5, která vyjadřuje vhodné pořadí pro zaplánování kapacit pro tento záznam osy S.
        /// Hodnotu 1 má příjem z výrobního příkazu; 2 má návrh na příjem pro reálnou poptávku (včetně kumulací); 3 návrh kvůli obchodnímu plánu, 4 pro MinZas, 5 bezdůvodně.
        /// </summary>
        public int PlanningProcessOrder
        {
            get
            {
                if (this.ChangeType == PlanSAxisChangeType.IncrementByRealProductOrder) return 1;    // 1: Fyzický výrobní příkaz
                if (this.IsForOriginReason(PlanSAxisOriginReason.RealRequest)) return 2;             // 2: Návrh, v němž je (samotná nebo kumulovaná) reálná poptávka
                if (this.IsForOriginReason(PlanSAxisOriginReason.PlanRequest)) return 3;             // 3: Návrh, v němž je (samotná nebo kumulovaná) poptávka obchodního plánu
                if (this.IsForOriginReason(PlanSAxisOriginReason.MinimumStock)) return 4;            // 4: Návrh, v němž je zajištění minimální zásob
                return 5;                                                                            // 5: Neznámý důvod
            }
        }
		/// <summary>{ get; } Sklad a plánovací jednotka</summary>
		public StockUnit StockUnit { get { return _StockUnit; } }
		private StockUnit _StockUnit;
		/// <summary>{ get; } GID způsobitele = vždy subjekt, zde se nepoužívá položkový GID</summary>
		public GID AuthorGId { get { return _AuthorGId; } }
		private GID _AuthorGId;
		/// <summary> { get; set; } SourceGID tohoto záznamu, pod ním je záznam k dohledání v indexu AxisHeap._AxisSourceIndex. Jde o </summary>
		internal GID SourceGId { get { return this._SourceGId; } set { this._SourceGId = value; } }
		private GID _SourceGId;
		/// <summary>{ get; } Klíč zdroje položky ze skladového dokladu</summary>
		public int Source { get { return _Source; } }
		private int _Source;
		/// <summary>{ get; } Klíč varianty z dokladu beze změn</summary>
		public SqlInt32 VariationDoc { get { return _VariationDoc; } }
		private SqlInt32 _VariationDoc;
		/// <summary>{ get; } Klíč varianty ze skladu (pokud je na stavu skladu povinná) nebo Null pokud není povinná.</summary>
		public SqlInt32 VariationPlan { get { return _VariationPlan; } }
		private SqlInt32 _VariationPlan;
		/// <summary>{ get; } Klíč varianty plánovací jednotky : pokud varianta není externí, pak Null, jinak = dle dokladu (ze skladu).</summary>
		public SqlInt32 VariationPus { get { return _VariationPus; } }
		private SqlInt32 _VariationPus;
		/// <summary>{ get; } Klíč varianty interní : pokud funkce potřebuje (i interní) výrobní variantu, je uvedena zde.</summary>
		public SqlInt32 VariationInt { get { return _VariationInt; } }
		private SqlInt32 _VariationInt;
		/// <summary>{ get; } Klíč zakázky z dokladu beze změn</summary>
		public SqlInt32 OrderRelationDoc { get { return _OrderRelationDoc; } }
		private SqlInt32 _OrderRelationDoc;
		/// <summary>{ get; } Klíč zakázky v plánovacím procesu (buď převzatý z dokladu, nebo Null pokud není povinný)</summary>
		public SqlInt32 OrderRelationPlan { get { return _OrderRelationPlan; } }
		private SqlInt32 _OrderRelationPlan;
		/// <summary>{ get; } Sklad z dokladu beze změn</summary>
		public int StockDoc { get { return _StockDoc; } }
		private int _StockDoc;
		/// <summary>{ get; } Sklad, na němž se plánuje</summary>
		public int StockPlan { get { return _StockPlan; } }
		private int _StockPlan;
		/// <summary>{ get; } Plánovat sklad</summary>
		public bool PlanovatSklad { get { return _PlanovatSklad; } }
		private bool _PlanovatSklad;
		/// <summary>{ get; } Útvar, kde má výroba proběhnout. Je určujícím klíčem při hledání T-modifikace.</summary>
		public SqlInt32 Depart { get { return _Depart; } }
		private SqlInt32 _Depart;
		/// <summary>{ get; } Výrobní příkaz, jehož finální výrobek je tímto záznamem osy přijímán, množství je akceptováno jako Zajištění, a plánují se potřebné kapacity pro chybějící množství na operacích.</summary>
		public SqlInt32 ProductOrder { get { return _ProductOrder; } }
		private SqlInt32 _ProductOrder;
		/// <summary>
		/// true, pokud je tento záznam připraven k zaplánování kapacit anebo jsou kapacity již zaplánovány
		/// </summary>
		public bool IsPreparedOrScheduled { get { return (this._ScheduledState == PlanSAxisSchedulingState.Prepared || this._ScheduledState == PlanSAxisSchedulingState.Scheduled); } }
		/// <summary>{ get; } Stav plánování kapacit výroby.</summary>
		public PlanSAxisSchedulingState ScheduledState { get { return _ScheduledState; } }
		private PlanSAxisSchedulingState _ScheduledState;
		/// <summary>{ get; } Časový interval práce na záznamu osy (this.DatePlanBegin ÷ this.DatePlanEnd). Pokud jeden z údajů není dosud naplněn, vrací TimeRange vytvořený z druhého údaje.</summary>
		public TimeRange DatePlanTime { get { return this._GetDatePlanTime(); } }
		/// <summary>
		/// Vrací časový interval práce na záznamu osy (this.DatePlanBegin ÷ this.DatePlanEnd).
		/// Pokud jeden z údajů není dosud naplněn, vrací TimeRange vytvořený z druhého údaje.
		/// Pokud jsou oba prázdné, vrací Empty.
		/// </summary>
		/// <returns></returns>
		private TimeRange _GetDatePlanTime()
		{
			if (this._DatePlanBegin > DateTime.MinValue && this._DatePlanEnd > DateTime.MinValue)
				return new TimeRange(this._DatePlanBegin, this._DatePlanEnd);
			else if (this._DatePlanBegin > DateTime.MinValue && this._DatePlanEnd == DateTime.MinValue)
				return new TimeRange(this._DatePlanBegin, this._DatePlanBegin);
			else if (this._DatePlanBegin == DateTime.MinValue && this._DatePlanEnd > DateTime.MinValue)
				return new TimeRange(this._DatePlanEnd, this._DatePlanEnd);
			return TimeRange.Empty;
		}
		/// <summary>{ get; set; } Datum požadavku na zahájení / dokončení výroby (podle směru registrace kapacit). Při běžném zpětném plánování obsahuje datum Min(požadavky) = k tomuto datu by mělo být k dispozici (z objednávky, z výrobního příkazu).</summary>
        internal DateTime? DatePlanRequest { get { return _DatePlanRequest; } set { _DatePlanRequest = value; } }
        private DateTime? _DatePlanRequest;
		/// <summary>{ get; set; } Datum požadavku na vydání finálu, vyskytuje se jen u finálních výdejů do poptávek (ChangeType = DecrementByPlanEnquiry nebo DecrementByRealEnquiry).</summary>
		internal DateTime? DateFinalRequest { get { return _DateFinalRequest; } set { _DateFinalRequest = value; } }
		private DateTime? _DateFinalRequest;
		/// <summary>{ get; } Datum dodání na sklad (příjem) / výdeje ze skladu (výdej) z podkladového dokladu (poptávka, objednávka, výrobní příkaz). Po načtení se již nezmění. Pro určení typu navrhované akce.</summary>
		internal DateTime? DateOriginal { get; private set; }
		/// <summary>{ get; set; } Směr plánování této položky</summary>
		internal TimeRange.TimeDirection DatePlanRequestDirection { get { return _DatePlanRequestDirection; } set { _DatePlanRequestDirection = value; } }
		private TimeRange.TimeDirection _DatePlanRequestDirection;
		/// <summary>{ get; } Datum, kdy bychom měli začít s plánovaným pohybem, abychom dodrželi dobu dodávky a rezervy a skončili v okamžik DatePlanEnd</summary>
		public DateTime DatePlanBegin { get { return _DatePlanBegin; } }
		private DateTime _DatePlanBegin;
		/// <summary>{ get; set; } Datum, kdy plánujeme ze skladu danou věc vydat/přijmout (kdy má být hotovo). Toto je datum, které vstupuje na skladovou kartu jako datum pohybu (ChangeDate) na položce pohybu StockChangeCls.</summary>
		public DateTime DatePlanEnd { get { return _DatePlanEnd; } set { _DatePlanEnd = value; } }
		private DateTime _DatePlanEnd;
		/// <summary>{ get; set; } Datum reálného počátku (datum zahájení výrobního příkazu). U ostatních pohybů je null.</summary>
        public SqlDateTime DateRealBegin { get { return _DateRealBegin; } set { _DateRealBegin = value; } }
		private SqlDateTime _DateRealBegin;
		/// <summary>{ get; set; } true = Datum na ose je fixní (například protože je zafixovaná operace (task), z něhož je tento záznam osy S příjmem). Tuto hodnotu lze měnit. Tato hodnota se mění ve funkci Fixace, a načítá do mapy vztahů a do extenderu.</summary>
		public bool IsFixedAxis { get { return _IsFixedAxis; } set { _IsFixedAxis = value; } }
		private bool _IsFixedAxis;
		/// <summary>{ get; } Číslo podkladové třídy</summary>
		public int AuthorClass { get { return _AuthorClass; } }
		private int _AuthorClass;
        /// <summary>{ get; } Kategorie požadavků, obsahuje kopii hodnoty this.ReqCategories ale jako SqlInt32</summary>
		public SqlInt32 RequestCategory { get { return (this.ReqCategories == null ? SqlInt32.Null : new SqlInt32(this.ReqCategories)); } }
		/// <summary>{ get; } Klíč podkladového záznamu - subjekt nebo nonsubjekt (skladový doklad, výrobní příkaz, komponenta, atd)</summary>
		public int AuthorSubj { get { return _AuthorSubj; } }
		private int _AuthorSubj;
		/// <summary>{ get; } Klíč položky skladového dokladu - objekt. Pokud podkladem není položková třída, je zde 0.</summary>
		public int AuthorObj { get { return _AuthorObj; } }
		private int _AuthorObj;
		/// <summary>{ get; } Vztah na AxisID záznamu parenta = příjem z výrobního příkazu, pro který je tento záznam komponentou.</summary>
		public int ParentID { get { return _ParentID; } }
		private int _ParentID;
		/// <summary>{ get; } Vztah na RecordNumber záznamu parenta = příjem z výrobního příkazu, pro který je tento záznam komponentou.</summary>
		public SqlInt32 ParentRecord { get { return _ParentRecord; } }
		private SqlInt32 _ParentRecord = SqlInt32.Null;
		/// <summary>{ get; } Plánovat kmenovou kartu</summary>
		public bool PlanovatKkzm { get { return _PlanovatKkzm; } }
		private bool _PlanovatKkzm;
		/// <summary>{ get; } Doba dodávky ze stavu skladu</summary>
		public int DeliveryTime { get { return _DeliveryTime; } }
		private int _DeliveryTime;
		/// <summary>{ get; } Doba dodávky - rezerva ze stavu skladu</summary>
		public int DeliveryTimeRes { get { return _DeliveryTimeRes; } }
		private int _DeliveryTimeRes;
		/// <summary>{ get; } Zda má tato kmenová karta zboží nastavenu variantu externí / interní (hodnota kkzm.var_ext)</summary>
		public bool VarExt { get { return _VarExt; } }
		private bool _VarExt;
		/// <summary>{ get; } true = na stavu skladu je varianta sledovanou dimenzí / false = nesledujeme (sledovat_varianta_zbozi = "A")</summary>
		public bool MonitoredVariation { get { return _MonitoredVariation; } }
		private bool _MonitoredVariation;
		/// <summary>{ get; } true = na stavu skladu je varianta povinnou dimenzí / false = nepovinná (povin_dim_varianta_zbozi = "A")</summary>
		public bool LiableVariation { get { return _LiableVariation; } }
		private bool _LiableVariation;
		/// <summary>{ get; } true = na stavu skladu je zakázka sledovanou dimenzí / false = nesledujeme (sledovat_obchodni_pripad = "A")</summary>
		public bool MonitoredOrderRelation { get { return _MonitoredOrderRelation; } }
		private bool _MonitoredOrderRelation;
		/// <summary>{ get; } true = na stavu skladu je zakázka povinnou dimenzí / false = nepovinná (povin_dim_obchodni_pripad = "A")</summary>
		public bool LiableOrderRelation { get { return _LiableOrderRelation; } }
		private bool _LiableOrderRelation;
		/// <summary>{ get; } Klíč aktuálního dodavatele ze stavu skladu, číslo záznamu plus číslo třídy</summary>
		public GID CurrentSupplierGID { get { return _CurrentSupplierGID; } }
		private GID _CurrentSupplierGID;
		/// <summary>{ get; } Množství položky, uvedené na dokladu</summary>
		public decimal Qty { get { return _Qty; } }
		private decimal _Qty;
		/// <summary>{ get; }
		/// Množství, které je odebrané z tohoto příjmu (pokud jsme příjmem) jako zajištění jiných požadavků.
		/// Výchozí hodnota je 0.
		/// Množství se sem přidává v průběhu balancování (metoda StockCardCls.GetAxisBalanceRelations())
		/// a v procesu načítání dat z databáze, při načítání vztahů (metoda AxisHeapCls._DataLoadRelation()).
		/// Množství slouží jako informace pro zjištění, zda tento záznam osy bude zobrazován v grafu Gantt Axis S 
		/// (zobrazovat se bude, pokud jde o výrobu, a něco z ní zbývá nespotřebovaného).
		/// </summary>
		public decimal QtyConsumed { get { return _QtyConsumed; } internal set { _QtyConsumed = value; } }
		private decimal _QtyConsumed;
		/// <summary>{ get; } Druh této položky, definuje zda jde o příjem, výdej a z jakého důvodu</summary>
		public PlanSAxisChangeType ChangeType { get { return _GetChangeType(); } }
		private PlanSAxisChangeType _ChangeType;
		/// <summary>{ get; } Důvod vzniku tohoto záznamu</summary>
		public PlanSAxisOriginReason OriginReason { get { return _OriginReason; } internal set { SetOriginReason(value); } }
		private PlanSAxisOriginReason _OriginReason;
		/// <summary>{ get; } 
		/// Způsob získání položky, která se touto položkou popisuje. 
		/// Pokud položku není třeba zajistit návrhem na výdej, je zde Needles. 
		/// Jinak je zde hodnota, odvozená ze třídy dodavatele a z přítomnosti dílce.
		/// <list type="">
		/// <listheader>Hodnoty a jejich důvody</listheader>
		/// <item>Needless (pokud není potřeba příjem zajistit, např. když jde o příjem jiný než Návrh na příjem)</item>
		/// <item>Manufacturing (pokud není znám dodavatel, a je znám dílec (T modifikace se netestuje))</item>
		/// <item>FromSupplier (pokud je znám dodavatel, a je to Organizace = třída 12)</item>
		/// <item>FromStock (pokud je znám dodavatel, a je to Sklad = třída 151)</item>
		/// <item>BadDefinition (pokud je třeba zajistit příjem, ale není známo odkud)</item>
		/// </list>
		/// </summary>
		public PlanSAxisAcquirementType Acquirement { get { return _Acquirement; } }
		private PlanSAxisAcquirementType _Acquirement;
		/// <summary>{ get; } Plánovací jednotka, její klíč</summary>
		public int PlanUnitS { get { return _PlanUnitS; } }
		private int _PlanUnitS;
		/// <summary>{ get; } Dílec VTPV, jeho klíč</summary>
		public int ConstrElement { get { return _ConstrElement; } }
		private int _ConstrElement;
		/// <summary>{ get; } T-modifikace, její klíč</summary>
		public int AOperationMod { get { return _AOperationMod; } }
		private int _AOperationMod;
		/// <summary>{ get; } Platnost údaje T-modifikace, aby nebylo nutno ji hledat stále opakovaně</summary>
		public bool AOperationModValidity { get { return _AOperationModValidity; } }
		private bool _AOperationModValidity;
		/// <summary>{ get; } Souhrn příznaků, které popisují rozšiřující údaje o záznamu osy S</summary>
		public PlanSAxisAttributes Attributes { get { return _Attributes; } }
		private PlanSAxisAttributes _Attributes;
		/// <summary>{ get; set; } Interní textový popisek, typicky nese Debug informaci v testech</summary>
		public string Description { get { return _Description; } internal set { _Description = value; } }
		private string _Description;
		/// <summary>
		/// Příznak, že tato položka osy S se má plánovat. 
		/// Hodnota true = default = položka se plánuje (plánuje se jak sklad, tak kmenová karta).
		/// Hodnota false = nastavená v MaterialData, značí že buď sklad nebo KKZM se neplánuje.
		/// Taková položka se nenavrhuje do akcí, nebalancuje, neukládá se na osu.
		/// </summary>
		internal bool EnablePlanning { get { return _EnablePlanning; } set { _EnablePlanning = value; } }
		private bool _EnablePlanning = true;
        /// <summary>
        /// Tento konkrétní záznam osy S povoluje akceptování ztrát na dílci. Default = true.
        /// Tato hodnota neobsahuje hodnoty z parametrů procesu (PlanCVersionData.MrpFixedLossCeReflect a PlanCVersionData.MrpPercentLossCeReflect).
        /// Na hodnotu false se nastavuje jen výjimečně při interaktivním přeplánování, když je zadáno výsledné množství (=se ztrátami na dílci).
        /// </summary>
        internal bool EnableLossCeReflect { get { return _EnableLossCeReflect; } set { _EnableLossCeReflect = value; } }
        private bool _EnableLossCeReflect = true;
		/// <summary>
		/// Soupis tasků, které patří k tomuto záznamu osy S.
		/// Pokud nejde o záznam o příjmu z výroby, je zde null.
		/// Jde o izolovanou kopii interního seznamu, změnou ve vráceném objektu nelze ovlivnit seznam uvnitř záznamu.
		/// </summary>
		public LinkedList<CapacityPlanWorkItemCls> TaskList { get { return (this._TaskList == null ? null : new LinkedList<CapacityPlanWorkItemCls>(this._TaskList)); } }
		/// <summary>
		/// Soupis tasku, které patří k tomuto záznamu osy S.
		/// Pokud nejde o záznam o příjmu z výroby, je zde null.
		/// Jde o přímou referenci na vnitřní seznam.
		/// </summary>
		internal LinkedList<CapacityPlanWorkItemCls> TaskListInternal { get { return this._TaskList; } set { this._TaskList = value; } }
		private LinkedList<CapacityPlanWorkItemCls> _TaskList;
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			if (!String.IsNullOrEmpty(Description))
				return Description;
			return "{AxisID=" + this.AxisID.ToString() + "; " + this.StockUnit.ToString() + "; Date=" + this.DatePlanTime.ToString() + "; " + ChangeType.ToString() + "; Qty=" + this.Qty + "}";
		}
        /// <summary>
        /// Reference: Název
        /// </summary>
        public string ReferNazev { get { return "#" + this.AxisID.ToString(); } }
        #endregion
		#region ODVOZENÉ PROPERTY, GREEN PROPERTY
		/// <summary>
		/// Změna množství na ose: plus = přírůstek, mínus = úbytek (Vrací Qty se znaménkem otočeným podle ChangeType)
		/// </summary>
		public decimal ChangeQty { get { return (PlanningComponentCls.AxisChangeTypeToCoefficient(this.ChangeType) * this.Qty); } }
        /// <summary>
        /// Obsahuje true, pokud se množství z tohoto záznamu osy má ukládat do "Počet požadavek" (true)
        /// anebo "Počet návrh" (false).
        /// </summary>
        public bool IsQtyRequest { get { return (PlanningComponentCls.AxisChangeTypeIsToRequestQty(this.ChangeType)); } }
        /// <summary>
		/// Obsahuje true, pokud tato změna je reálná a ne jen plánovaná
		/// </summary>
		public bool IsRealItem { get { return PlanningComponentCls.AxisChangeTypeIsReal(this.ChangeType); } }
        /// <summary>
        /// Obsahuje true, pokud tato změna je plánovaná (návrhy + obchodní plán) a ne reálná (vstupní doklady)
        /// </summary>
        public bool IsPlanItem { get { return PlanningComponentCls.AxisChangeTypeIsPlan(this.ChangeType); } }
        /// <summary>
		/// Obsahuje true, pokud tato změna je návrhem (vygenerovaný záznam v procesu plánování), ne vstupujícím pohybem (false = skladový doklad včetně poptávky obchodního plánu / výrobní příkaz / komponenta)
		/// </summary>
        public bool IsProposal { get { return PlanningComponentCls.AxisChangeTypeIsProposal(this.ChangeType); } }
		///<summary><para>Atribut: Typ dispozice [mfr_prj_vyd] {"P"=Příjem; "V"=Výdej}</para><para>Db: lcs.plan_unit_s_axis.type (char (1) not null)</para></summary>
		public string Type { get { return PlanningComponentCls.AxisChangeTypeToStringPV(this.ChangeType) ; }}
		///<summary><para>Atribut: Způsob vzniku záznamu [mfr_mrp_type_rec] {"OO"=OO (poptávka); "DO"=DO (objednávka); "PDO"=PDO (potvrzená objednávka); "VP"=VP (výrobní příkaz); "KVP"=KVP (komponenty VP); "ROZ"=ROZ (Rozpad); "NP"=NP (Návrh příjem); "NV"=NV (Návrh výdej); "MM"=MM (minimum/maximum)}</para><para>Db: lcs.plan_unit_s_axis.type_rec (varchar (3) not null)</para></summary>
		public string TypeRec { get { return PlanningComponentCls.AxisChangeTypeToStringType(this.ChangeType); } }
		///<summary><para>Atribut: Požadováno [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty (numeric_19_6 not null)</para></summary>
		public decimal QtyRequest { get { return (this.IsQtyRequest ? this.Qty : 0M); } }
		///<summary><para>Atribut: Návrh [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_prop (numeric_19_6 not null)</para></summary>
		public decimal QtyProp { get { return (this.IsQtyRequest ? 0M : this.Qty ); } }
        ///<summary>Atribut: Návrh navýšený o ztráty (pokud není null) nebo množství (this.Qty)</summary>
        public decimal QtyPropWithLossNn { get { return (!this._QtyPropWithLoss.IsNull ? this._QtyPropWithLoss.Value : this.Qty); } }
        ///<summary><para>Atribut: Návrh navýšený o ztráty [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_prop (numeric_19_6 not null)</para></summary>
        public SqlDecimal QtyPropWithLoss { get { return this._QtyPropWithLoss; } set { this._QtyPropWithLoss = value; } }
        private SqlDecimal _QtyPropWithLoss;
        ///<summary><para>Atribut: Vyřízeno [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_prop_real (numeric_19_6 not null)</para></summary>
		public decimal QtyPropReal { get { return (this.IsQtyRequest ? 0M : this.Qty); } }
		///<summary><para>Atribut: Návrh upravený [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_prop_adj (numeric_19_6 not null)</para></summary>
		public decimal QtyPropAdj { get { return (this.IsQtyRequest ? 0M : this.Qty); } set { _QtyPropAdj = value; } }
		private decimal _QtyPropAdj;
		/// <summary>Pořadové číslo návrhu, usnadňuje pozdější třídění osy v případě více návrhů v jeden okamžik</summary>
		internal int ProposalSeq { get; set; }
		/// <summary>Pořadové číslo dávky v návrhu, usnadňuje pozdější třídění osy v případě více návrhů v jeden okamžik</summary>
		internal int ProposalSub { get; set; }
		/// <summary>
		/// Navrhovaná akce pro tento záznam za jeho současného stavu
		/// </summary>
		public PlanSAxisSuggestedActionType SuggestedAction { get { return this._GetSuggestedAction(); } }
		/// <summary>
		/// Metoda zjistí, zda lze tento záznam přesunout na jiné (dané) datum.
		/// Jde o datum dokončení.
		/// Přesunout lze tehdy, když záznam není fixovaný, a pokud je to záznam o výrobě, pak nesmí být zaplánovaný do kapacit.
		/// </summary>
		/// <param name="targetDateTime"></param>
		/// <returns></returns>
		internal bool AllowedMoveDateTo(DateTime targetDateTime)
		{
			// I FIXOVANÉ záznamy dovolím přesouvat (Jirka Hrubý, Karel Virt):
			//     if (this.DateFixed) return false;
			if (this.IsReceiptFinalProduct && this.ScheduledState == PlanSAxisSchedulingState.Scheduled) return false;
			return true;
		}
		/// <summary>
		/// Metoda vrátí datum na původní hodnotu (načtenou z podkladového dokladu).
		/// Použije si v okamžiku, kdy skladová karta zahajuje tvorbu návrhů, a předchozí posuny času v této situaci ztrácí význam.
		/// Vracíme se k výchozím hodnotám.
		/// </summary>
		internal void MoveDateReset()
		{
			// Pokud jde o záznam o příjmu z výroby (návrh, reálný VP, objednávka), a je již zaplánovaný do kapacit,
			//  pak jej nebudeme posouvat:
			if (this.IsReceiptFinalProduct && this.ScheduledState == PlanSAxisSchedulingState.Scheduled) return;

			// Datum vrátím jen pro záznamy pocházející z reálné objednávky a z reálného VP:
			if ((this.ChangeType == PlanSAxisChangeType.IncrementByRealSupplierOrder || this.ChangeType == PlanSAxisChangeType.IncrementByRealProductOrder) &&
                 this.DateOriginal.HasValue)
			{
				if (this.DatePlanEnd != this.DateOriginal.Value)
					this.DatePlanEnd = this.DateOriginal.Value;
			}
		}

		// Průběžné stavy zásoby celkem za PJ:
		///<summary><para>Atribut: K dispozici PJ [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_d (numeric_19_6 not null)</para></summary>
		public decimal QtyD { get { return _QtyD; } set { _QtyD = value; } }
		private decimal _QtyD = 0M;
		///<summary><para>Atribut: K dispozici PJ D+N [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_dp (numeric_19_6 not null)</para></summary>
		public decimal QtyDp { get { return _QtyDp; } set { _QtyDp = value; } }
		private decimal _QtyDp = 0M;
		///<summary><para>Atribut: K dispozici PJ D+N,P [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_dp_c (numeric_19_6 not null)</para></summary>
		public decimal QtyDpC { get { return _QtyDpC; } set { _QtyDpC = value; } }
		private decimal _QtyDpC = 0M;
		///<summary><para>Atribut: Pořadí osy [editgeneral]</para><para>Db: lcs.plan_unit_s_axis.axis_seq (int not null)</para></summary>
		public int AxisSeq { get { return _AxisSeq; } set { _AxisSeq = value; } }
		private int _AxisSeq;

		// Průběžné stavy zásoby na skladu:
		///<summary><para>Atribut: K dispozici PJ sklad [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_st_d (numeric_19_6 not null)</para></summary>
		public decimal QtyStD { get { return _QtyStD; } internal set { _QtyStD = value; } }
		private decimal _QtyStD = 0M;
		///<summary><para>Atribut: K dispozici PJ sklad D+N [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_st_dp (numeric_19_6 not null)</para></summary>
		public decimal QtyStDp { get { return _QtyStDp; } internal set { _QtyStDp = value; } }
		private decimal _QtyStDp = 0M;
		///<summary><para>Atribut: K dispozici PJ sklad D+N,P [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_st_dp_c (numeric_19_6 not null)</para></summary>
		public decimal QtyStDpC { get { return _QtyStDpC; } set { _QtyStDpC = value; } }
		private decimal _QtyStDpC = 0M;            // Poznámka: tato hodnota zůstane == 0, protože se nikdy nevyplňuje !!!
		///<summary><para>Atribut: Zajištění PJ sklad D+N [editnumeric_19_6]</para><para>Db: lcs.plan_unit_s_axis.qty_st_dp2 (numeric_19_6 not null)</para></summary>
		public decimal QtyStDp2 { get { return _QtyStDp2; } internal set { _QtyStDp2 = value; } }
		private decimal _QtyStDp2 = 0M;
		///<summary><para>Atribut: axis_seq2 [editgeneral]</para><para>Db: lcs.plan_unit_s_axis.axis_seq2 (int not null)</para></summary>
		public int AxisSeq2 { get { return _AxisSeq2; } internal set { _AxisSeq2 = value; } }
		private int _AxisSeq2;

		///<summary><para>Atribut: Převod [typ_ano_ne] {"A"=Ano; "N"=Ne}</para><para>Db: lcs.plan_unit_s_axis.transfer (char (1) not null)</para></summary>
		public string Transfer { get { return (_IsTransfer ? "A" : "N"); } set { _IsTransfer = (!String.IsNullOrEmpty(value) && value == "A"); } }
		///<summary>Transfer: true je pouze u návrhu na výdej ze skladu, jindy je false</summary>
		public bool IsTransfer { get { return _IsTransfer; } set { _IsTransfer = value; } }
		private bool _IsTransfer;

		///<summary><para>Vztah 9096: Komponenta kusovníku PJ (zprava: PJ S osa)</para><para>Db: lcs.plan_unit_s_axis.a_structure (int null)</para></summary>
		public int AStructure { get { return _AStructure; } set { _AStructure = value; } }
		private int _AStructure;
		///<summary><para>Vztah 9322: Komponenta VP (zprava: Pj s osa)</para><para>Db: lcs.plan_unit_s_axis.product_order_structure (int null)</para></summary>
		public int ProductOrderStructure { get { return _ProductOrderStructure; } set { _ProductOrderStructure = value; } }
		private int _ProductOrderStructure;
		///<summary><para>Vztah 100637: Katalogové číslo (zprava: PJS osa)</para><para>Db: lcs.plan_unit_s_axis.catalog_number (int null)</para></summary>
		public int CatalogNumber { get { return _CatalogNumber; } set { _CatalogNumber = value; } }
		private int _CatalogNumber;
		///<summary><para>Vztah 100653: Ref. číslo OP (zprava: PJ S Osa)</para><para>Db: lcs.plan_unit_s_axis.reference_number (int null)</para></summary>
		public int ReferenceNumber { get { return _ReferenceNumber; } set { _ReferenceNumber = value; } }
		private int _ReferenceNumber;
		///<summary><para>Vztah 100939: Kategorie požadavku (zprava: Plánovací jednotka S osa)</para><para>Db: lcs.plan_unit_s_axis.req_categories (int null)</para></summary>
		public int ReqCategories { get { return _ReqCategories; } set { _ReqCategories = value; } }
		private int _ReqCategories;
		///<summary><para>Atribut: Navrhovaná akce [mfr_proposal_action] {"n"=Žádná akce; "v"=Vytvořit VP nebo objednávku; "z"=Zrušit VP nebo objednávku; "m"=Snížit množství; "t"=Upravit termín; "k"=Snížit množství a upravit termín}</para><para>Db: lcs.plan_unit_s_axis.action (char (1) null)</para></summary>
		public string Action { get { return EnumConvert.TypeToDb(this.SuggestedAction); } }
		///<summary><para>Atribut: Návrh potvrzen [typ_ano_ne] {"A"=Ano; "N"=Ne}</para><para>Db: lcs.plan_unit_s_axis.conc_conf (char (1) not null)</para></summary>
		public string ConcConf { get { return (_IsConcConf ? "A" : "N"); } set { _IsConcConf = String.Equals(value, "A", StringComparison.Ordinal); } }
		/// <summary>
		/// Je návrh potvrzen?
		/// </summary>
		public bool IsConcConf { get { return _IsConcConf; } set { _IsConcConf = value; } }
		private bool _IsConcConf;
		///<summary><para>Atribut: Charakter požadavku [mfr_int_ext] {"I"=Interní; "E"=Externí}</para><para>Db: lcs.plan_unit_s_axis.ext_int (char (1) not null)</para></summary>
		public string ExtInt { get { return _GetExtInt(); } }
		///<summary><para>Atribut: Stavový příznak 1</para><para>Db: lcs.plan_unit_s_axis.marker1 (char (1) not null)</para>
        ///<para>
        ///p - datum převzato z DatumPlán
        ///D - datum převzato z Datum (datum_plan bylo nezadané)
        ///v - datum převzato z výrobního příkazu (datum ukončení)
        ///k - datum převzato z komponenty výrobního příkazu (datum zahájení)
        ///</para>
        ///</summary>
		public string Marker1
        {
            get
            {
                if (this.IsFromWhDoc)
                {
                    if ((int)(this._Attributes & PlanSAxisAttributes.DateFromPlan) > 0) return "p";
                    return "D";
                }
                if (this.IsFromProductOrder)
                {
                    return "v";
                }
                if (this.IsFromProductOrderStructure)
                {
                    return "k";
                }
                return "-";
            }
        }
		///<summary><para>Atribut: Stavový příznak 2</para><para>Db: lcs.plan_unit_s_axis.marker2 (char (1) not null)</para>
        ///<para>
        ///a - alternativa byla dohledána náhradním algoritmem (s nejmenší velikostí dávky)
        ///A - alternativa nebyla dohledána ani náhradním algoritmem - záznam je nekonzistetní, bez T modifikace
        ///</para>
        ///</summary>
		public string Marker2
        {
            get
            {
                if ((int)(this._Attributes & PlanSAxisAttributes.AlternativeFoundForMinBatchQty) > 0) return "a";
                if ((int)(this._Attributes & PlanSAxisAttributes.AlternativeNotFound) > 0) return "A";
                return "-";
            }
        }
		///<summary><para>Atribut: Stavový příznak 3</para><para>Db: lcs.plan_unit_s_axis.marker3 (char (1) not null)</para>
        ///<para>
        ///H - historické datum výběru posunuté na aktuální systémové pro výběr T modifikace při dohledání rozpadu
        ///</para>
        ///</summary>
		public string Marker3
        {
            get
            {
                return "-";
            }
        }
		///<summary><para>Atribut: Stavový příznak 4</para><para>Db: lcs.plan_unit_s_axis.marker4 (char (1) not null)</para>
        ///<para>
        ///(nevyužito)
        ///</para>
        ///</summary>
        public string Marker4
        {
            get
            {
                return "-";
            }
        }
		///<summary><para>Atribut: Stavový příznak 5</para><para>Db: lcs.plan_unit_s_axis.marker5 (char (1) not null)</para>
        ///<para>
        ///b - vzniklo inicializací z poptávky obchodního plánu (business plan)
        ///</para>
        ///</summary>
        public string Marker5
        {
            get
            {
                if ((int)(this._Attributes & PlanSAxisAttributes.BussinesPlanEnquiry) > 0) return "b";
                return "-";
            }
        }
		///<summary><para>Atribut: Stavový příznak 6</para><para>Db: lcs.plan_unit_s_axis.marker6 (char (1) not null)</para>
        ///<para>
        ///n - native - původní požadavek úrovně (Může generovat požadavky do stejné úrovně, které ale již nejsou nativní)
        ///</para>
        ///</summary>
        public string Marker6
        {
            get
            {
                if (this.IsFromOriginalRecord) return "n";
                return "-";
            }
        }
		///<summary><para>Atribut: Stavový příznak 7</para><para>Db: lcs.plan_unit_s_axis.marker7 (char (1) not null)</para>
        ///<para>
        ///E - Error zapsaný do MRP protokolu navázaný na aktuální záznam
        ///</para>
        ///</summary>
        public string Marker7
        {
            get
            {
                return "-";
            }
        }
		///<summary><para>Atribut: Stavový příznak 8</para><para>Db: lcs.plan_unit_s_axis.marker8 (char (1) not null)</para>
        ///<para>
        ///p - proposal (návrh)
        ///r - request  (požadavek)
        ///</para>
        ///</summary>
        ///<remarks>
        ///p (proposal) vzniká pouze tak, že při bilancování PJ proti určitému skladu (až přijde její LLC na řadu) dojde k jeho nedostatku 
        ///   a je na tutéž osu založen plánovaný příjem, který tento nedostatek pokryje.
        ///r (request) Naproti tomu potřeba komponent do sestav vniká dříve než jsou tyto bilancovány 
        ///   a eviduje se jako požadavek (request) bez ohledu na to, je-li v daném čase na skladě k dispozici nebo ne. 
        ///   Requesty se zakládají do jiných os, než která je aktuálně bilancována.
        ///</remarks>
        public string Marker8
        {
            get
            {
                if (this.IsProposal)
                {   // Jen návrhy:
                    if (this.IsIncrement) return "p";       // Návrh na příjem
                    if (this.IsDecrement) return "r";       // Návrh na výdej
                }
                return "-";
            }
        }
		/// <summary>
		/// Vrací typ změny na tomto záznamu osy.
		/// Vrátí this._ChangeType s výjimkou případu, kdy je this._ChangeType == PlanSAxisChangeType.IncrementByOrderProduction 
		/// (tj. objednávka do výroby), a přitom není znám výrobní postup anebo nejsme na výrobním skladu.
		/// </summary>
		/// <returns></returns>
		private PlanSAxisChangeType _GetChangeType()
		{
			PlanSAxisChangeType changeType = this._ChangeType;
			if (changeType == PlanSAxisChangeType.IncrementByOrderProduction && (this._AOperationMod == 0 || !this._PlanovatSklad))
				// Pokud je uloženo "Objednávka z výroby", ale neznám TModifikaci nebo se sklad nemá plánovat, změním typ na Objednávka reálná:
				changeType = PlanSAxisChangeType.IncrementByRealSupplierOrder;
			return changeType;
		}
		/// <summary>
		/// Vrátí příznak I=interní / E=externí
		/// </summary>
		/// <returns></returns>
		private string _GetExtInt()
		{
			PlanSAxisChangeType changeType = this._ChangeType;
			if (changeType == PlanSAxisChangeType.DecrementByRealEnquiry ||
				((changeType == PlanSAxisChangeType.IncrementByPlanSupplierOrder || changeType == PlanSAxisChangeType.IncrementByRealSupplierOrder || changeType == PlanSAxisChangeType.IncrementByOrderProduction)
					&& this._CurrentSupplierGID.ClassNumber == Constants.ClassNumberOrganizace))
				return "E";
			return "I";
		}
		/// <summary>
		/// Vrátí info, zda tato položka má být zobrazena v Gantt grafu osy S na plánovací tabuli.
		/// Do grafu patří A: poptávky (reálné i poptávky obchodního plánu),
		/// a rovněž B: příjmy z výrobních příkazů (existující, plánované, objednané), 
		/// pokud z jejich množství něco zůstane na skladu.
		/// </summary>
		/// <returns></returns>
		private bool _GetIsForGraphAxisS()
		{
			// Na osu se dostávají výdeje do poptávek (reálné i obchodní plán), a příjmy z objednávek do výroby:
			PlanSAxisChangeType changeType = this.ChangeType;
			if (changeType == PlanSAxisChangeType.DecrementByRealEnquiry ||
				changeType == PlanSAxisChangeType.DecrementByPlanEnquiry ||
				changeType == PlanSAxisChangeType.IncrementByOrderProduction)
				return true;

			// Na osu se dostávají příjmy z výrobních příkazů, pokud z nich zbylo nějaké množství, které zůstane na skladě
			//   (množství z VP není plně balancováno do jiných požadavků):
			if (changeType == PlanSAxisChangeType.IncrementByRealProductOrder ||
				changeType == PlanSAxisChangeType.IncrementByPlanProductOrder ||
				changeType == PlanSAxisChangeType.IncrementByOrderProduction)
				return (this.Qty > this.QtyConsumed);

			// Ostatní položky osy se do grafu nedávají:
			return false;
		}
		/// <summary>
		/// Určí navrhovanou akci
		/// </summary>
		/// <returns></returns>
		private PlanSAxisSuggestedActionType _GetSuggestedAction()
		{
			// Pro výdeje z osy nedělám návrhy (zatím nenavrhuji změnu termínu například u poptávky):
			if (this.ChangeQty <= 0M)
				return PlanSAxisSuggestedActionType.None;

			// Jde tedy o příjem.

			// Vedlejší produkty: nic.
			if (this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductDissonant ||
				this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductSuitable ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductDissonant ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductSuitable)
				return PlanSAxisSuggestedActionType.None;
				
			// Návrhy na příjem (VP, objednávka, obecný návrh): "Vytvořit VP nebo objednávku":
			if (this.IsActionForNewRecord)
				return PlanSAxisSuggestedActionType.IssueNewRecord;

			// Příjem z reálné objednávky / výrobního příkazu:
			if (this.IsActionForExistsRecord)
			{	// Určíme rozdíl mezi datem z podkladu a datem na ose, a určíme skutečně potřebné množství pro požadavky:
                if (this.IsActionDelete)
					return PlanSAxisSuggestedActionType.Delete;

                bool reduceQty = this.IsActionChangeQty;   //  (this.Qty > requestQty);
                bool changeDate = this.IsActionChangeDate; //  (this.DateOriginal.HasValue && this.DatePlanEnd != this.DateOriginal.Value);
				if (!reduceQty && !changeDate)
					return PlanSAxisSuggestedActionType.NoAction;
				if ( reduceQty && !changeDate)
					return PlanSAxisSuggestedActionType.ChangeQty;
				if (!reduceQty &&  changeDate)
					return PlanSAxisSuggestedActionType.ChangeDate;
				if (reduceQty && changeDate)
					return PlanSAxisSuggestedActionType.ChangeDateAndQty;
			}

			// Nic (ale sem bych neměl dojít):
			return PlanSAxisSuggestedActionType.None;
		}
        /// <summary>
        /// Máme doporučit smazání?
        /// </summary>
        public bool IsActionDelete
        {
            get
            {
                return (this.IsActionForExistsRecord && this.QtyConsumed <= 0m);
            }
        }
        /// <summary>
        /// Máme doporučit změnu množství?
        /// </summary>
        public bool IsActionChangeQty
        {
            get
            {
                return (this.IsActionForExistsRecord && this.QtyConsumed > 0m && this.Qty > this.QtyConsumed);
            }
        }
        /// <summary>
        /// Máme doporučit změnu data?
        /// </summary>
        public bool IsActionChangeDate
        {
            get
            {
                if (!this.IsActionForExistsRecord) return false;
                if (this.IsFixedAxis)
                    // Na fixovaném záznamu je DatePlanEnd == DateOriginal (to tedy neřeším), ale DatePlanRequest obsahuje datum prvního z požadavků oproti DatePlanEnd (což je datum objednávky nebo dokončení VP):
                    return (this.DatePlanRequest.HasValue && this.DatePlanRequest.Value != this.DatePlanEnd);

                // Nefixovaný záznam má nastaveno DatePlanEnd = DatePlanRequest, takže porovnáme DatePlanRequest proti DateOriginal:
                return (this.DateOriginal.HasValue && this.DatePlanRequest.HasValue && this.DateOriginal.Value != this.DatePlanRequest.Value);
            }
        }
        /// <summary>
        /// Máme doporučovat nějakou akci pro existující záznamy? Pouze pro příjem z VP nebo z objednávky!
        /// </summary>
        public bool IsActionForExistsRecord
        { get { return this.IsAnyOfChangeType(PlanSAxisChangeType.IncrementByRealSupplierOrder, PlanSAxisChangeType.IncrementByRealProductOrder); } }
        /// <summary>
        /// Máme doporučovat akci Založit záznam?
        /// </summary>
        public bool IsActionForNewRecord
        { get { return this.IsAnyOfChangeType(PlanSAxisChangeType.IncrementByPlanSupplierOrder, PlanSAxisChangeType.IncrementByPlanProductOrder, PlanSAxisChangeType.IncrementByProposalReceipt, PlanSAxisChangeType.IncrementByPlanStockTransfer); } }
        /// <summary> { get; }
		///  Typ T dokumentu (VTPV / STPV / VP), odvozeno podle ChangeType: 
        ///  { DecrementByRealComponent, IncrementByRealProductOrder, IncrementByRealByProductSuitable, IncrementByRealByProductDissonant } = Production;
        ///  { DecrementByPlanComponent, IncrementByPlanProductOrder, IncrementByPlanByProductSuitable, IncrementByPlanByProductDissonant } = PlanApproved;
        ///  jinak None.
		///  </summary>
		public TDocumentType DocumentType
		{
			get
			{
				switch (this.ChangeType)
				{
					case PlanSAxisChangeType.DecrementByRealComponent:
                    case PlanSAxisChangeType.IncrementByRealProductOrder:
                    case PlanSAxisChangeType.IncrementByRealByProductSuitable:
                    case PlanSAxisChangeType.IncrementByRealByProductDissonant:
						return TDocumentType.Production;
					case PlanSAxisChangeType.DecrementByPlanComponent:
                    case PlanSAxisChangeType.IncrementByPlanProductOrder:
                    case PlanSAxisChangeType.IncrementByPlanByProductSuitable:
                    case PlanSAxisChangeType.IncrementByPlanByProductDissonant:
						return TDocumentType.PlanApproved;
				}
				return TDocumentType.None;
			}
		}
		/// <summary> { get; }
		/// Klíč T komponenty (číslo komponenty VTPV, T modifikace STPV, Výrobního příkazu), podle typu dokumentu</summary>
		public int StructureKey
		{
			get
			{
				switch (this.ChangeType)
				{
					case PlanSAxisChangeType.DecrementByRealComponent:
						return this.ProductOrderStructure;
					case PlanSAxisChangeType.DecrementByPlanComponent:
						return this.AStructure;
				}
				return 0;
			}
		}
		#endregion
		#region TVORBA OBJEKTU S PŘEVZETÍM HODNOT Z PARAMETRŮ
		/// <summary>
		/// Vytvoří a vrátí objekt této třídy na základě předaných dat
		/// </summary>
		/// <param name="datePlanEnd">Plánované datum dokončení pohybu</param>
		/// <param name="changeType">Typ změny = důvod</param>
		/// <param name="qty">Množství změny</param>
		/// <param name="dateFixed">Položka má fixované datum, nelze ji přesunout v čase</param>
		/// <param name="stockUnit">Klíč MRP skladu a plánovací jednotky S</param>
		/// <param name="authorDoc">Klíč podkladového dokladu (subjektový GID: kladné číslo třídy, kladné číslo subjektu dokladu)</param>
		/// <param name="authorObj">Klíč podkladové položky (číslo objektu položky skladového dokladu) nebo 0 u nepoložkových záznamů</param>
		/// <returns></returns>
		internal static MaterialPlanAxisItemCls CreateFrom(DateTime datePlanEnd, PlanSAxisChangeType changeType, decimal qty, bool dateFixed, StockUnit stockUnit, GID authorDoc, int authorObj)
		{
			return _CreateFrom(datePlanEnd, changeType, qty, dateFixed, stockUnit, authorDoc, authorObj);
		}
		/// <summary>
		/// Vytvoří a vrátí objekt této třídy na základě předaných dat
		/// </summary>
		/// <param name="datePlanEnd">Plánované datum pohybu</param>
		/// <param name="changeType">Typ změny = důvod</param>
		/// <param name="qty">Množství změny</param>
		/// <param name="dateFixed">Položka má fixované datum, nelze ji přesunout v čase</param>
		/// <param name="authorClass">Číslo třídy podkladu</param>
		/// <param name="authorSubj">Číslo subjektu podkladu</param>
		/// <param name="authorObj">Klíč podkladové položky (číslo objektu položky skladového dokladu) nebo 0 u nepoložkových záznamů</param>
		/// <returns></returns>
		internal static MaterialPlanAxisItemCls CreateFrom(DateTime datePlanEnd, PlanSAxisChangeType changeType, decimal qty, bool dateFixed, int authorClass, int authorSubj, int authorObj)
		{
			return _CreateFrom(datePlanEnd, changeType, qty, dateFixed, StockUnit.Empty, new GID(authorClass, authorSubj), authorObj);
		}
		/// <summary>
		/// Vytvoří a vrátí objekt této třídy na základě předaných dat
		/// </summary>
		/// <param name="datePlanEnd">Plánované datum pohybu</param>
		/// <param name="changeType">Typ změny = důvod</param>
		/// <param name="qty">Množství změny</param>
		/// <param name="isFixedAxis">Položka má fixované datum, nelze ji přesunout v čase</param>
		/// <param name="authorDoc">Klíč podkladového dokladu (subjektový GID: kladné číslo třídy, kladné číslo subjektu dokladu)</param>
		/// <param name="authorObj">Klíč podkladové položky (číslo objektu položky skladového dokladu) nebo 0 u nepoložkových záznamů</param>
		/// <returns></returns>
		internal static MaterialPlanAxisItemCls CreateFrom(DateTime datePlanEnd, PlanSAxisChangeType changeType, decimal qty, bool isFixedAxis, GID authorDoc, int authorObj)
		{
			return _CreateFrom(datePlanEnd, changeType, qty, isFixedAxis, StockUnit.Empty, authorDoc, authorObj);
		}
		/// <summary>
		/// Vytvoří a vrátí objekt této třídy na základě předaných dat
		/// </summary>
		/// <param name="datePlanEnd">Plánované datum dokončení pohybu</param>
		/// <param name="changeType">Typ změny = důvod</param>
		/// <param name="qty">Množství změny</param>
		/// <param name="isFixedAxis">Položka má fixované datum, nelze ji přesunout v čase</param>
		/// <param name="stockUnit">Klíč MRP skladu a plánovací jednotky S</param>
		/// <param name="authorDoc">Klíč podkladového dokladu (subjektový GID: kladné číslo třídy, kladné číslo subjektu dokladu)</param>
		/// <param name="authorObj">Klíč podkladové položky (číslo objektu položky skladového dokladu) nebo 0 u nepoložkových záznamů</param>
		/// <returns></returns>
		private static MaterialPlanAxisItemCls _CreateFrom(DateTime datePlanEnd, PlanSAxisChangeType changeType, decimal qty, bool isFixedAxis, StockUnit stockUnit, GID authorDoc, int authorObj)
		{
			MaterialPlanAxisItemCls axisItem = new MaterialPlanAxisItemCls();
			if (stockUnit.NotEmpty)
				axisItem.SetStockUnit(stockUnit);
			axisItem.SetAuthor(authorDoc.ClassNumber, authorDoc.RecordNumber, authorObj);
			axisItem._DatePlanEnd = datePlanEnd;
            axisItem._IsFixedAxis = isFixedAxis;
			axisItem._ChangeType = changeType;
			axisItem._Qty = qty;
			axisItem._QtyPropAdj = 0M;
			axisItem._DetectAcquirement();

			if (axisItem.IsFromOriginalRecord)
				axisItem.DateOriginal = datePlanEnd;         // Pro záznamy pocházející z reálného podkladu vepíšu i datum originálu.
			return axisItem;
		}
		#endregion
		#region ODVOZENÉ PROPERTY, SETOVACÍ METODY
		/// <summary>
		/// Ukládá do objektu číslo skladu a plánovací jednotku
		/// </summary>
		/// <param name="stockDoc">Číslo skladu externí (nemusí být MRP)</param>
		/// <param name="stockPlan">Číslo skladu plánovací MRP</param>
		/// <param name="planUnitS">Klíč plánovací jednotky S</param>
		internal void SetStockUnit(int stockDoc, int stockPlan, int planUnitS)
		{
			StockUnit stockUnit = new StockUnit(stockPlan, planUnitS);
			this.SetStockUnit(stockUnit);
			this._StockDoc = stockDoc;
		}
		/// <summary>
		/// Ukládá do objektu číslo skladu a plánovací jednotku
		/// </summary>
		/// <param name="stockUnit">Spojený klíč MRP skladu a PlanUnitS</param>
		internal void SetStockUnit(StockUnit stockUnit)
		{
			if (stockUnit.IsEmpty)
				Throw.SysError(MessageInfo.Get("Nelze změnit klíč skladu a plánovací jednotky na prázdnou hodnotu."));
			if (this._StockUnit.NotEmpty && !this._StockUnit.Equals(stockUnit))
				Throw.SysError(MessageInfo.Get("Nelze změnit klíč skladu a plánovací jednotky poté, kdy byl přidělen."));

			this._StockPlan = stockUnit.StockMrp;
			this._PlanUnitS = stockUnit.PlanUnitS;
			this._StockUnit = stockUnit;
		}
		/// <summary>
		/// Ukládá do objektu autora (způsobitele)
		/// </summary>
		/// <param name="authorClass">Číslo třídy, má být kladné</param>
		/// <param name="authorSubj">Číslo subjektu</param>
		/// <param name="authorObj">Číslo položky</param>
		internal void SetAuthor(int authorClass, int authorSubj, int authorObj)
		{
			this._AuthorClass = authorClass;
			this._AuthorSubj = authorSubj;
			this._AuthorObj = authorObj;
			this._AuthorGId = new GID(authorClass, authorSubj);
		}
		/// <summary>
		/// Na osu vepíše vztah na parentID záznam osy (AxisID).
		/// Vepisuje se pouze na záznamy typu Komponenta nebo Vedlejší produkt.
		/// </summary>
		/// <param name="parentID">Hodnota ParentID = číslo AxisID záznamu parent</param>
		internal void SetParentID(int parentID)
		{
			if (this.ChangeType == PlanSAxisChangeType.DecrementByPlanComponent ||
				this.ChangeType == PlanSAxisChangeType.DecrementByRealComponent ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductSuitable ||
				this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductSuitable ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductDissonant ||
				this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductDissonant)
				this._ParentID = parentID;
			else
				Throw.SysError(MessageInfo.Get("Proběhl pokus o vepsání klíče ParentID %0 na záznam typu %1, což není povolený typ.", parentID, this.ChangeType));
		}
		/// <summary>
		/// Na osu vepíše vztah na parent záznam osy (Axis.RecordNumber).
		/// Vepisuje se pouze na záznamy typu Komponenta nebo Vedlejší produkt.
		/// </summary>
		/// <param name="parentRecord">Hodnota ParentRecordNumber</param>
		internal void SetParentRecordNumber(int parentRecord)
		{
			if (this.ChangeType == PlanSAxisChangeType.DecrementByPlanComponent ||
				this.ChangeType == PlanSAxisChangeType.DecrementByRealComponent ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductSuitable ||
				this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductSuitable ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductDissonant ||
				this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductDissonant)
				this._ParentRecord = parentRecord;
			else
				Throw.SysError(MessageInfo.Get("Proběhl pokus o vepsání klíče ParentID %0 na záznam typu %1, což není povolený typ.", parentRecord, this.ChangeType));
		}
		/// <summary>
		/// Nastaví počáteční datum do tohoto záznamu, do položky podle směru registrace (timeDir).
		/// Pro směr ToFuture nastaví DatePlanBegin, pro směr ToHistory nastaví DatePlanEnd.
		/// </summary>
		/// <param name="date">Datum, které se bude vkládat</param>
		/// <param name="timeDir">Směr registrace času, směr směr ToFuture nastaví datum do DatePlanBegin, pro směr ToHistory do DatePlanEnd.</param>
		internal void SetAxisDateEdge(DateTime date, TimeRange.TimeDirection timeDir)
		{
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					this._DatePlanBegin = date;
					break;
				case TimeRange.TimeDirection.ToHistory:
					this._DatePlanEnd = date;
					break;
			}
		}
		/// <summary>
		/// Nastaví datum položky DatePlanBegin = datum, ke kterému se má začít s pohybem, tak aby skončil v cílový okamžik DatePlanEnd.
		/// </summary>
		/// <param name="datePlanBegin"></param>
		internal void SetAxisDateBegin(DateTime datePlanBegin)
		{
			this._DatePlanBegin = datePlanBegin;
		}
		/// <summary>
		/// Nastaví datum položky DatePlanEnd = datum, ke kterému bude položka uvedena na skladové kartě (datum fyzického pohybu).
		/// </summary>
		/// <param name="datePlanEnd"></param>
		internal void SetAxisDateEnd(DateTime datePlanEnd)
		{
			this._DatePlanEnd = datePlanEnd;
		}
		/// <summary>
		/// Nastaví datum položky DatePlanBegin a DatePlanEnd podle předaného intervalu TimeRange.
		/// Korektnost dat se nijak nekontroluje.
		/// </summary>
		/// <param name="timeRange">Zadání hodnot pro DatePlanBegin a DatePlanEnd</param>
		internal void SetAxisDateRange(TimeRange timeRange)
		{
			this._DatePlanBegin = timeRange.Begin;
			this._DatePlanEnd = timeRange.End;
		}
		/// <summary>
		/// Nastaví datum položky DatePlanBegin = DatePlanEnd mínus dny (dodávka + rezerva)
		/// </summary>
		private void _SetAxisDateBeginFromEnd()
		{
			int delivery = this.DeliveryTime + this.DeliveryTimeRes;
			if (delivery < 0) delivery = 0;
			this._DatePlanBegin = this.DatePlanEnd.AddDays(-delivery);
		}
        /// <summary>
        /// Pokud tento záznam osy je o příjmu z VP nebo objednávky, pak nastaví dané datum jako DatePlanRequest.
        /// Pokud není fixovaný, pak nastaví i datum DatePlanEnd.
        /// Sloučí důvody vzniku záznamu (OriginReason) (pro uvedené druhy záznamu = příjmy).
        /// </summary>
        /// <param name="minIssueDate"></param>
        internal void SetDateEndFromMinIssue(DateTime minIssueDate, PlanSAxisOriginReason originReason)
        {
            if (this.IsAnyOfChangeType(
                     PlanSAxisChangeType.IncrementByOrderProduction, PlanSAxisChangeType.IncrementByPlanProductOrder, PlanSAxisChangeType.IncrementByPlanStockTransfer, PlanSAxisChangeType.IncrementByPlanSupplierOrder,
                     PlanSAxisChangeType.IncrementByProposalReceipt, PlanSAxisChangeType.IncrementByRealProductOrder, PlanSAxisChangeType.IncrementByRealSupplierOrder))
            {
                // Posunout datum DatePlanRequest k datu nejnižšího návrhu:
                this._DatePlanRequest = minIssueDate;
                if (!this.IsFixedAxis)
                    // U nefixovaných záznamů (typicky výrobní příkazy bez fixace): změnit datum plánovaného dokončení:
                    this._DatePlanEnd = minIssueDate;
                if (originReason != PlanSAxisOriginReason.NoReason)
                    // Vložit důvod vzniku záznamu:
                    this.SetOriginReason(originReason);
            }
        }
		/// <summary>
		/// Určí datum podle směru času: 
		/// pro směr ToFuture: určí podle DateBegin čas DateEnd,
		/// pro směr ToHistory: určí podle DateEnd čas DateBegin.
		/// Výchozím je datum uložené v this, anebo parametr.
		/// </summary>
		/// <param name="timeDir">Směr času</param>
		/// <param name="initialDate">Výchozí čas, bude uložen do odpovídajícího času (pro ToFuture do DateBegin, pro ToHistory do DateEnd).</param>
		internal void TimeCalculate(DateTime initialDate, TimeRange.TimeDirection timeDir)
		{
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					this._DatePlanBegin = initialDate;
					this.TimeCalculate(timeDir);
					break;
				case TimeRange.TimeDirection.ToHistory:
					this._DatePlanEnd = initialDate;
					this.TimeCalculate(timeDir);
					break;
				default:
					Throw.SysError(MessageInfo.Get("Nelze určit lhůtu záznamu na ose S pro směr času jiný než ToFuture a ToHistory. Směr času = %0.", ((int)timeDir).ToString()));
					break;
			}
		}
		/// <summary>
		/// Určí datum podle směru času: 
		/// pro směr ToFuture: určí podle DateBegin čas DateEnd,
		/// pro směr ToHistory: určí podle DateEnd čas DateBegin.
		/// Výchozím je datum uložené v this, anebo parametr.
		/// </summary>
		/// <param name="timeDir">Směr času.Musí být ToFuture nebo ToHistory.</param>
		internal void TimeCalculate(TimeRange.TimeDirection timeDir)
		{
			double days = 0D;
			// Pokud jde o příjem z výroby, pak vložím interval = 0 sekund.
			// To navazuje na interaktivní režim, kdy se posouvají záznamy v režimu ToHistory:
			//   vztah AxisBalance => do záznamu osy Prev (Axis = příjem z výroby) 
			//     se vloží požadované datum příjmu na sklad (z časově následného záznamu osy S o výdeji),
			//     a poté se v dalším kroku (podle vztahu TaskToAxis) bude kontrolovat 
			//     čas End navázaného (podřízeného, Prev) tasku = finální operace, a případně upravovat = přeregistrovávat, 
			//     a to k času počátku na této ose.
			// Aby to fungovalo, pak se při kalkulaci času na záznamu o příjmu z výroby ukládá čas = 0, tzn. Begin = End:
			if (this.ChangeType == PlanSAxisChangeType.IncrementByPlanProductOrder ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealProductOrder ||
				this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductSuitable ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductSuitable ||
				this.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductDissonant ||
				this.ChangeType == PlanSAxisChangeType.IncrementByRealByProductDissonant)
			{
				days = 0D;
			}
			else if (this.ChangeQty < 0M)
			{	// Výdeje jsou hotové hnedka:
				days = 0D;
			}
			else
			{	// Varianta základní = doba dodání + rezerva:
				days = this.DeliveryTime + this.DeliveryTimeRes;
			}

			if (days < 0D) days = 0D;

			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					if (this._DatePlanBegin.Year > 1900)
						this._DatePlanEnd = this._DatePlanBegin.AddDays(days);
					break;
				case TimeRange.TimeDirection.ToHistory:
					if (this._DatePlanEnd.Year > 1900)
						this._DatePlanBegin = this._DatePlanEnd.AddDays(-days);
					break;
				default:
					Throw.SysError(MessageInfo.Get("Nelze určit lhůtu záznamu na ose S pro směr času jiný než ToFuture a ToHistory. Směr času = %0.", ((int)timeDir).ToString()));
					break;
			}
		}
		/// <summary>
		/// Naplní časový vektor DatePlanRequest + DatePlanRequestDirection
		/// </summary>
		/// <param name="date"></param>
		/// <param name="timeDir"></param>
		internal void SetRequestTimeVector(DateTime date, TimeRange.TimeDirection timeDir)
		{
			this.DatePlanRequest = date;
			this.DatePlanRequestDirection = timeDir;
		}
		/// <summary>
		/// Na osu vepíše data o zdroji, variantě, zakázce a skladu.
		/// Vynuluje plánovací jednotku a StockUnit.
		/// </summary>
		/// <param name="source">Klíč: Kmenová karta</param>
		/// <param name="stock">Klíč: Sklad ze skladového dokladu</param>
		/// <param name="variation">Klíč: Varianta ze skladového dokladu</param>
		/// <param name="orderRelation">Klíč: Zakázka ze skladového dokladu</param>
		internal void SetMaterial(int source, SqlInt32 variation, SqlInt32 orderRelation, int stock)
		{
			SetMaterial(source, variation, orderRelation, stock, false);
		}
		/// <summary>
		/// Na osu vepíše data o zdroji, variantě, zakázce a skladu.
		/// Vynuluje plánovací jednotku a StockUnit.
		/// </summary>
		/// <param name="source">Klíč: Kmenová karta</param>
		/// <param name="stock">Klíč: Sklad ze skladového dokladu</param>
		/// <param name="variation">Klíč: Varianta ze skladového dokladu</param>
		/// <param name="orderRelation">Klíč: Zakázka ze skladového dokladu</param>
		/// <param name="setToMfrDimension">Požadavek: true = propsat zde zadané dimenze (varianta, zakázka) i do všech navázaných výrobních dimenzí záznamu osy S</param>
		internal void SetMaterial(int source, SqlInt32 variation, SqlInt32 orderRelation, int stock, bool setToMfrDimension)
		{
			this._Source = source;
			this._VariationDoc = variation;
			this._OrderRelationDoc = orderRelation;
			this._StockDoc = stock;
			this._StockPlan = stock;

			if (setToMfrDimension)
			{
				this._VariationInt = variation;
				this._VariationPlan = variation;
				this._VariationPus = variation;
				this._OrderRelationPlan = orderRelation;
			}

			this._PlanUnitS = 0;
			this._StockUnit = StockUnit.Empty;
		}
		/// <summary>
		/// Naplní zdroj a sklad, z předaných objektů doplní další potřebné údaje do záznamu osy
		/// </summary>
		/// <param name="kmenovaKartaSkladu">Data o zboží</param>
		/// <param name="skSklad">Data o skladu</param>
		/// <param name="stavSk">Data o stavu skladu (skladový pohled)</param>
		internal void SetSource(KmenovaKartaSkladuCls kmenovaKartaSkladu, SkSkladCls skSklad, StavSkCls stavSk)
		{
			this._Source = kmenovaKartaSkladu.RecordNumber;
			this._VarExt = (kmenovaKartaSkladu.VarExt == "A");
			this._PlanovatKkzm = (kmenovaKartaSkladu.Planovat == "A");

            if (this._StockDoc == 0)
			    this._StockDoc = skSklad.RecordNumber;
			this._StockPlan = skSklad.RecordNumber;
			this._PlanovatSklad = (skSklad.Planovat == "A");
			this._Depart = skSklad.Utvar;

			this._DeliveryTime = stavSk.Dodavka;
			this._DeliveryTimeRes = stavSk.Rezerva;
			this._CurrentSupplierGID = GID.Empty;
			if (!stavSk.AktualniDodavatel.IsNull && !stavSk.AktualniDodavatelClass.IsNull)
				this._CurrentSupplierGID = new GID(stavSk.AktualniDodavatelClass.Value, stavSk.AktualniDodavatel.Value);
			this._MonitoredVariation = (stavSk.SledovatVariantaZbozi == "A");
			this._LiableVariation = (stavSk.PovinDimVariantaZbozi == "A");
			this._MonitoredOrderRelation = (stavSk.SledovatObchodniPripad == "A");
			this._LiableOrderRelation = (stavSk.PovinDimObchodniPripad == "A");

			// Povolit plánování podle kmenové karty a skladu:
			this._EnablePlanning = (this.PlanovatKkzm && this.PlanovatSklad);

			// Datum zahájení pohybu: nějaký čas před jeho dokončením:
            if (this._DatePlanBegin == DateTime.MinValue)
                this.TimeCalculate(TimeRange.TimeDirection.ToHistory);

			this._DetectAcquirement();
		}
		/// <summary>
		/// Naplní dimenze zadané, a dimenze plánované (pokud jsou povinné).
		/// Plní tedy: VariationDoc, VariationPlan, OrderRelationDoc, OrderRelationPlan.
		/// </summary>
		/// <param name="variation"></param>
		/// <param name="orderRelation"></param>
		internal void SetDimension(SqlInt32 variation, SqlInt32 orderRelation)
		{
			this._VariationDoc = variation;                                                                  // Varianta načtená z položky dokladu
			this._VariationPlan = (variation.IsNull || !this.LiableVariation ? SqlInt32.Null : variation);   // Varianta vstupující do plánování
			this._VariationPus = (this.VarExt ? this._VariationPlan : SqlInt32.Null);                        // Varianta plánovací jednotky (externí)
			this._OrderRelationDoc = orderRelation;                                                          // Zakázka načtená z položky dokladu
			this._OrderRelationPlan = (orderRelation.IsNull || !this.LiableOrderRelation ? SqlInt32.Null : orderRelation);
		}
		/// <summary>
		/// Naplní dimenzi VariationInt.
		/// </summary>
		/// <param name="variationInt"></param>
		internal void SetDimensionVarInt(SqlInt32 variationInt)
		{
			this._VariationInt = variationInt;
		}
		/// <summary>
		/// Naplní klíč plánovací jednotky PlanUnitS.
		/// </summary>
		/// <param name="planUnitS">Klíč plánovací jednotky</param>
		internal void SetPlanUnitS(int planUnitS)
		{
			SetStockUnit(new StockUnit(this.StockPlan, planUnitS));
		}
		/// <summary>
		/// Naplní klíč výrobního příkazu.
		/// </summary>
		/// <param name="productOrder">Klíč výrobního příkazu</param>
		internal void SetProductOrder(int productOrder)
		{
			this._ProductOrder = (productOrder > 0 ? new SqlInt32(productOrder) : SqlInt32.Null);
		}
		/// <summary>
		/// Do záznamu osy S vepíše vztah na komponentu kusovníku (STPV / VP) podle zadaného T dokumentu
		/// </summary>
		/// <param name="tStructure">Data o komponentě</param>
		internal void SetStructure(IStructureData tStructure)
		{
			this._AStructure = (tStructure.DocumentType == TDocumentType.PlanApproved ? tStructure.Structure : 0);
			this._ProductOrderStructure = (tStructure.DocumentType == TDocumentType.Production ? tStructure.Structure : 0);
		}
		/// <summary>
		/// Vynuluje daný atribut (nebo všechny, pokud nebude zadáno) = pokud je v this.Attributes obsažen, pak již nebude.
		/// </summary>
		internal void ResetAttributes()
		{
			this._Attributes = (PlanSAxisAttributes)0;
		}
		/// <summary>
		/// Vynuluje daný atribut = pokud je v this.Attributes obsažen, pak již nebude.
		/// </summary>
		/// <param name="attribute">Požadovaný atribut. Pokud nebude zadáno, pak vynuluje celou hodnotu this.Attributes.</param>
		internal void ResetAttributes(PlanSAxisAttributes attribute)
		{
			SetAttributes(attribute, false);
		}
		/// <summary>
		/// Nastaví daný atribut v this.Attributes na hodnotu "Ano" nebo na danou hodnotu.
		/// </summary>
		/// <param name="attribute">Požadovaný atribut</param>
		internal void SetAttributes(PlanSAxisAttributes attribute)
		{
			SetAttributes(attribute, true);
		}
		/// <summary>
		/// Nastaví daný atribut v this.Attributes na hodnotu "Ano" nebo na danou hodnotu.
		/// </summary>
		/// <param name="attribute">Požadovaný atribut</param>
		/// <param name="value">Požadovaná hodnota: true = default = nastavit hodnotu / false = nulovat hodnotu</param>
		internal void SetAttributes(PlanSAxisAttributes attribute, bool value)
		{
			PlanSAxisAttributes current = this.Attributes;
			if (value)
				this._Attributes = (current | attribute);
			else
			{
				PlanSAxisAttributes reset = (PlanSAxisAttributes)((int)attribute ^ Int32.MaxValue);
				this._Attributes = (current & reset);
			}
		}
		/// <summary>
		/// Nastaví (přidá) důvod vzniku tohoto záznamu do this.OriginReason.
		/// Pokud je na vstupu hodnota PlanSAxisOriginReason.NoReason, pak se do vloží tato hodnota (přepíše se aktuální hodnota) do this.OriginReason.
		/// Pokud je na vstupu jiná hodnota než PlanSAxisOriginReason.NoReason, pak se přičte (or) k současné hodnotě.
		/// </summary>
		/// <param name="reason"></param>
		internal void SetOriginReason(PlanSAxisOriginReason reason)
		{
			if (reason == PlanSAxisOriginReason.NoReason)
				this._OriginReason = PlanSAxisOriginReason.NoReason;
			else
				this._OriginReason |= reason;
		}
		/// <summary>
		/// Z osy odebere výrobní definice (dílec, T modifikace), aby bylo možno vepsat nové hodnoty
		/// </summary>
		internal void ResetTpvData()
		{
			this._ConstrElement = 0;
			this._AOperationMod = 0;
			this._AOperationModValidity = false;
			this._DetectAcquirement();
		}
		/// <summary>
		/// Nastaví do záznamu osy S data pro výrobu.
		/// Setuje hodnoty ConstrElement, AOperationMod, AOperationModValidity = true;
		/// </summary>
		/// <param name="constrElement">Plná data o výsledném dílci</param>
		/// <param name="aOperationMod">Plná data o T modifikaci STPV</param>
		internal void SetTpvData(ConstrElementCls constrElement, AOperationModCls aOperationMod)
		{
			if (this.ConstrElement != 0)
				Throw.SysError(MessageInfo.Get("Nelze změnit výrobní údaje poté, kdy byly přiděleny."));
			this._ConstrElement = (constrElement == null ? 0 : constrElement.RecordNumber);
			this._AOperationMod = (aOperationMod == null ? 0 : aOperationMod.RecordNumber);
			this._AOperationModValidity = true;
			this._DetectAcquirement();
		}
		/// <summary>
		/// Nastaví do záznamu osy S data pro výrobu.
		/// Setuje hodnoty ConstrElement, AOperationMod, AOperationModValidity = true;
		/// </summary>
		/// <param name="constrElement">Klíč výsledného dílce</param>
		/// <param name="aOperationMod">Klíč T modifikace STPV</param>
		internal void SetTpvData(SqlInt32 constrElement, SqlInt32 aOperationMod)
		{
			if (this.ConstrElement != 0)
				Throw.SysError(MessageInfo.Get("Nelze změnit výrobní údaje poté, kdy byly přiděleny."));
			this._ConstrElement = (constrElement.IsNull ? 0 : constrElement.Value);
			this._AOperationMod = (aOperationMod.IsNull ? 0 : aOperationMod.Value);
			this._AOperationModValidity = true;
			this._DetectAcquirement();
		}
		/// <summary>
		/// Nastaví hodnotou atributu Typ změny.
		/// Je povoleno pouze z hodnoty IncrementByProposalReceipt na hodnotu IncrementByPlanProductOrder.
		/// </summary>
		/// <param name="changeType"></param>
		internal void SetChangeType(PlanSAxisChangeType changeType)
		{
			if (this.ChangeType == PlanSAxisChangeType.IncrementByProposalReceipt && (changeType == PlanSAxisChangeType.IncrementByPlanProductOrder || changeType == PlanSAxisChangeType.IncrementByPlanSupplierOrder || changeType == PlanSAxisChangeType.IncrementByPlanStockTransfer))
				this._ChangeType = changeType;
			else
				Throw.SysError(MessageInfo.Get("Došlo k pokusu o nepovolenou změnu hodnoty MaterialPlanAxisItemCls.ChangeType z hodnoty %0 na hodnotu %1.", this.ChangeType, changeType));
		}
		/// <summary>
		/// Nastaví danou hodnotu do property ProductionScheduled.
		/// </summary>
		internal void SetScheduledState(PlanSAxisSchedulingState state)
		{
			if (this.ChangeType == PlanSAxisChangeType.IncrementByPlanProductOrder || this.ChangeType == PlanSAxisChangeType.IncrementByRealProductOrder || this.ChangeType == PlanSAxisChangeType.IncrementByOrderProduction)
				this._ScheduledState = state;
		}
		/// <summary>
		/// Určí způsob případného získávání tohoto materiálu (do this.Acquirement).
		/// Předem potřebuje mít vyplněny hodnoty: Qty, ChangeType, CurrentSupplier, CurrentSupplierClass, ConstrElement.
		/// Určí hodnotu: 
		/// this.Acquirement = Needless (pokud není potřeba příjem zajistit, např. když jde o příjem jiný než Návrh na příjem)
		/// this.Acquirement = Manufacturing (pokud není znám dodavatel, a je znám dílec (T modifikace se netestuje))
		/// this.Acquirement = FromSupplier (pokud je znám dodavatel, a je to Organizace = třída 12)
		/// this.Acquirement = FromStock (pokud je znám dodavatel, a je to Sklad = třída 151)
		/// this.Acquirement = BadDefinition (pokud je třeba zajistit příjem, ale není známo odkud)
		/// </summary>
		private void _DetectAcquirement()
		{
			PlanSAxisAcquirementType acquirement = PlanSAxisAcquirementType.Needless;
			PlanSAxisChangeType changeType = this.ChangeType;
			bool isManufactured = (this.ConstrElement != 0);                                              // Umíme to vyrobit ?
			bool isOrderProd = (isManufactured && this.ChangeType == PlanSAxisChangeType.IncrementByOrderProduction); // Objednávka do výroby
			bool isAnySuppl = this._CurrentSupplierGID.NotEmpty;                                          // Máme nějakého dodavatele?
			bool isFromSuppl = (this._CurrentSupplierGID.ClassNumber == Constants.ClassNumberOrganizace); // Máme dodavatele == Organizace?
			bool isFromStock = (this._CurrentSupplierGID.ClassNumber == SkSkladCls.ClassNr);              // Máme dodavatele == Sklad ?
			bool needAcquire = (this.Qty > 0M && changeType == PlanSAxisChangeType.IncrementByProposalReceipt);

			if (!needAcquire)                    // Není třeba zajišťovat
				acquirement = PlanSAxisAcquirementType.Needless;
			else if (isOrderProd)                // Objednaná výroba
				acquirement = PlanSAxisAcquirementType.Manufacturing;
			else if (isFromStock)                // Předisponovat z jiného skladu
				acquirement = PlanSAxisAcquirementType.FromStock;
			else if (isFromSuppl)                // Objednat od organizace
				acquirement = PlanSAxisAcquirementType.FromSupplier;
			else if (isManufactured)             // Umíme to vyrobit
				acquirement = PlanSAxisAcquirementType.Manufacturing;
			else
				acquirement = PlanSAxisAcquirementType.BadDefinition;

			this._Acquirement = acquirement;

			if (acquirement == PlanSAxisAcquirementType.Manufacturing)
			{	// Pro výrobní pohyby nebude stav plánování None, ale NotScheduled:
				if (this._ScheduledState == PlanSAxisSchedulingState.None)
					this._ScheduledState = PlanSAxisSchedulingState.NotScheduled;
			}
			else
			{	// Pro nevýrobní pohyby nebude stav plánování NotScheduled, ale None:
				if (this._ScheduledState == PlanSAxisSchedulingState.NotScheduled)
					this._ScheduledState = PlanSAxisSchedulingState.None;
			}
		}
		/// <summary>
		/// Odebere Task ze seznamu zdejších tasků. Pokud neexistuje, nic nedělá.
		/// </summary>
		/// <param name="taskItem"></param>
		internal void TaskRemove(CapacityPlanWorkItemCls taskItem)
		{
			if (taskItem != null && this._TaskList != null)
				this._TaskList.Remove(taskItem);
		}
		#endregion
		#region VYTVOŘENÍ OBJEKTU S NÁVRHEM NA PŘÍJEM/VÝDEJ NA ZÁKLADĚ POŽADAVKU NA VÝDEJ/PŘÍJEM
		/// <summary>
		/// Vytvoří klon dodaného objektu, a tomu klonu pak přidělí nové ID a nastaví v něm PlanningState = PlanSAxisPlanningState.Active;
		/// </summary>
		/// <param name="axisOriginal">Podkladový záznam</param>
		/// <param name="isCumulated">Návrh vzniká kumulací = na výsledku je třeba vymazat data, která se při kumulaci mažou (Author, Description, ReferenceNumber).</param>
		/// <returns></returns>
		protected static MaterialPlanAxisItemCls CreateNewItem(MaterialPlanAxisItemCls axisOriginal, bool isCumulated)
		{
			MaterialPlanAxisItemCls clone = (MaterialPlanAxisItemCls)axisOriginal.MemberwiseClone();

			// Položky, které nepřebírám z podkladu:
			clone._IsTransfer = false;
			clone.DateOriginal = null;
            clone.EnableLossCeReflect = true;           // Výchozí hodnota = true

			// Položky, které nepřebírám (nuluji) v případě kumulovaného návrhu:
			if (isCumulated)
			{	// Kumulace požadavků z více položek => vztah na autora (Způsobitel) se odebere, 
				// návrh na příjem nemůže mít jeden statický vztah na více způsobitelů z různých požadavků:
				clone._AuthorClass = 0;
				clone._AuthorGId = GID.Empty;
				clone._AuthorObj = 0;
				clone._Description = null;
				clone._ReferenceNumber = 0;
			}

			// Musím vygenerovat nové ID:
			clone.SetNextID();
			clone._PlanningState = PlanSAxisPlanningState.Active;

			return clone;
		}
		/// <summary>
		/// Vytvoří návrh na příjem na základě daného požadavku, množství, data příjmu a příznaku kumulace.
		/// Návrh na příjem se vytváří pro tu samou skladovou kartu, na které je požadvek. Nelze přeci příjem dát na jinou kartu, než kde je potřeba.
		/// Opačná situace je v metodě CreateRequest(), která zakládá návrh na výdej z dodavatelského skladu, více viz tam.
		/// </summary>
		/// <param name="axisRequest">Data o požadavku. Nesmí být null</param>
		/// <param name="proposalQty">Množství návrhu</param>
		/// <param name="targetDate">Datum potřebného příjmu na skladovou kartu</param>
		/// <param name="isCumulated">Příznak, že požadované množství je kumulované z více různých požadavků. Pak axisRequest představuje první požadavek ze všech kumulovaných.</param>
		/// <returns>Data o návrhu na příjem</returns>
		internal static MaterialPlanAxisItemCls CreateProposalFromRequest(MaterialPlanAxisItemCls axisRequest, decimal proposalQty, DateTime targetDate, bool isCumulated)
		{
			if (axisRequest == null)
				Throw.SysError(MessageInfo.Get("Nelze vytvořit záznam materiálové osy pro Návrh na příjem na základě požadavku, který je null."));

			// Nová věta vytvořená kopií:
			MaterialPlanAxisItemCls axisProposal = MaterialPlanAxisItemCls.CreateNewItem(axisRequest, isCumulated);

			// Některé hodnoty změním, některé vynuluji:
			axisProposal._Acquirement = PlanSAxisAcquirementType.BadDefinition;
			axisProposal._AOperationMod = 0;
			axisProposal._AOperationModValidity = false;
			axisProposal._Attributes = PlanSAxisAttributes.Empty;
            axisProposal._IsFixedAxis = false;
			axisProposal._DatePlanBegin = DateTime.MinValue;           // Bude určeno v procesu plnění záznamu osy (metoda axis.SetSource())
			axisProposal._DatePlanEnd = targetDate;
			axisProposal._DatePlanRequest = targetDate;
			axisProposal._DatePlanRequestDirection = TimeRange.TimeDirection.ToHistory;
			axisProposal._ChangeType = PlanSAxisChangeType.IncrementByProposalReceipt;
			axisProposal._Qty = proposalQty;
			axisProposal._QtyPropAdj = proposalQty;
            axisProposal.DateOriginal = null;
	
			// Další hodnoty dopočítám:
			axisProposal._DetectAcquirement();

			return axisProposal;
		}
		/// <summary>
		/// Vytvoří návrh na příjem na základě daného návrhu ze skladové karty z procesu plánování.
		/// </summary>
		/// <param name="proposal">Veškerá data o návrhu</param>
		/// <returns>Data o návrhu na příjem</returns>
		internal static MaterialPlanAxisItemCls CreateProposalFromPlanProp(PlanningStockCardProposalCls proposal)
		{
			if (proposal == null)
				Throw.SysError(MessageInfo.Get("Nelze vytvořit záznam materiálové osy pro Návrh na příjem na základě podkladu, který je null."));
			if (proposal.AxisItemRequest == null)
				Throw.SysError(MessageInfo.Get("Nelze vytvořit záznam materiálové osy pro Návrh na příjem na základě podkladu, který neobsahuje požadavek."));

			// Základ vytvoříme obecnější metodou:
			MaterialPlanAxisItemCls axisProposal = CreateProposalFromRequest(proposal.AxisItemRequest, proposal.Qty,  proposal.Time, proposal.ProposalIsCombined);

			// Doplníme další konkrétní data z návrhu:
			axisProposal._AuthorClass = proposal.AuthorGId.ClassNumber;
			axisProposal._AuthorGId = proposal.AuthorGId;
			axisProposal._AuthorSubj = proposal.AuthorGId.RecordNumber;
			axisProposal._AuthorObj = proposal.AuthorObj;
			axisProposal._OriginReason = proposal.OriginReason;
			axisProposal.ProposalSeq = proposal.ProposalSeq;
			axisProposal.ProposalSub = proposal.ProposalSub;
	
			return axisProposal;
		}
		/// <summary>
		/// Vytvoří návrh na příjem na základě předaného původního návrhu na příjem, kde změní čas, množství a dodavatele.
		/// Používá se při přeplánování osy S, při změně množství anebo druhu zajištění / času atd.
		/// Po vytvoření návrhu je třeba na něj doplnit T modifikaci, původní se odebrala. 
		/// Důvod: došlo ke změně množství a termínu, což má vliv na volbu T modifikace.
		/// </summary>
		/// <param name="axisOriginal">Data o původním návrhu. Nesmí být null.</param>
		/// <param name="proposalQty">Množství návrhu</param>
		/// <param name="targetDate">Datum potřebného příjmu na skladovou kartu</param>
		/// <param name="isCumulated">Příznak, že požadované množství je kumulované z více různých požadavků. Pak axisRequest představuje první požadavek ze všech kumulovaných.</param>
		/// <param name="currentSupplier">Aktuální dodavatel pro tento záznam osy S (pokud je Empty, pak se nevloží Empty ale ponechá se původní dodavatel na ose !)</param>
		/// <returns>Data o návrhu na příjem</returns>
		internal static MaterialPlanAxisItemCls CreateProposalFromProposal(MaterialPlanAxisItemCls axisOriginal, decimal proposalQty, DateTime targetDate, bool isCumulated, GID currentSupplier)
		{
			if (axisOriginal == null)
				Throw.SysError(MessageInfo.Get("Nelze vytvořit záznam materiálové osy pro Návrh na příjem na základě požadavku, který je null."));

			// Nová věta vytvořená kopií:
			MaterialPlanAxisItemCls axisProposal = MaterialPlanAxisItemCls.CreateNewItem(axisOriginal, isCumulated);

			// Některé hodnoty změním, některé vynuluji:
			axisProposal._Acquirement = PlanSAxisAcquirementType.BadDefinition;             // Způsob zajištění dosud není určen
			axisProposal._AOperationMod = 0;
			axisProposal._AOperationModValidity = false;
			axisProposal._Attributes = PlanSAxisAttributes.Empty;
            axisProposal._IsFixedAxis = false;
			axisProposal._DatePlanBegin = targetDate;
			axisProposal._DatePlanEnd = targetDate;
			axisProposal._DatePlanRequest = targetDate;
			axisProposal._DatePlanRequestDirection = TimeRange.TimeDirection.ToHistory;
			axisProposal._ChangeType = PlanSAxisChangeType.IncrementByProposalReceipt;      // Obecný návrh na příjem, bez specifikace podle druhu zajištění
			axisProposal._Qty = proposalQty;
			axisProposal._QtyPropAdj = proposalQty;
			axisProposal._ScheduledState = PlanSAxisSchedulingState.None;

			// Aktuální dodavatel:
			if (!currentSupplier.IsEmpty)
				axisProposal._CurrentSupplierGID = currentSupplier;

			// Další hodnoty dopočítám:
			axisProposal._DetectAcquirement();           // Zjistíme, jakým způsobem lze sehnat tuhle věc (Nakoupit? Přinést ze skladu? Vyrobit? Ukrást?)

			return axisProposal;
		}
		/// <summary>
		/// Vytvoří požadavek na výdej z dodavatelského skladu.
		/// Vytvořený záznam má platné pouze údaje o zdroji, variantě a zakázce (axisSupply.Source, axisSupply.VariationDoc, axisSupply.OrderRelationDoc).
		/// Dále má platné údaje o kmenové kartě (axisSupply.PlanovatKkzm, axisSupply.VarExt).
		/// Z podkladu dále přebírá Způsobitele, Kategorii, Množství.
		/// Nepřebírá (nuluje), je třeba nově dohledat: TMod, Dílec, Plánovací jednotku, StockUnit, Útvar, Dimenze ze stavu skladu, interní údaje o zakázce a variantě.
		/// </summary>
		/// <param name="axisProposal">Záznam o návrhu na příjem, který potřebujeme vydat z dodavatelského skladu</param>
		/// <param name="stock">Klíč dodavatelského skladu</param>
		/// <returns></returns>
		internal static MaterialPlanAxisItemCls CreateRequestSupply(MaterialPlanAxisItemCls axisProposal, int stock)
		{
			if (axisProposal == null)
				Throw.SysError(MessageInfo.Get("Nelze vytvořit záznam materiálové osy pro Návrh na výdej na základě záznamu, který je null."));

			// Nová věta vytvořená kopií:
			MaterialPlanAxisItemCls axisSupply = MaterialPlanAxisItemCls.CreateNewItem(axisProposal, false);

			// Některé hodnoty změním, některé vynuluji:
			axisSupply._Acquirement = PlanSAxisAcquirementType.Needless;
			axisSupply._AOperationMod = 0;
			axisSupply._AOperationModValidity = false;
			axisSupply._Attributes = PlanSAxisAttributes.Empty;
			axisSupply._ConstrElement = 0;
			axisSupply._CurrentSupplierGID = GID.Empty;
            axisSupply._IsFixedAxis = false;
			axisSupply._DatePlanEnd = axisProposal.DatePlanBegin;
			axisSupply._DatePlanBegin = axisProposal._DatePlanBegin;
			axisSupply._DeliveryTime = 0;
			axisSupply._DeliveryTimeRes = 0;
			axisSupply._Depart = SqlInt32.Null;
			axisSupply._Description = null;
			axisSupply._ChangeType = PlanSAxisChangeType.DecrementByProposalRequisition; // PlanSAxisChangeType.DecrementByPlanEnquiry;
			axisSupply._LiableOrderRelation = false;
			axisSupply._LiableVariation = false;
			axisSupply._MonitoredOrderRelation = false;
			axisSupply._MonitoredVariation = false;
			axisSupply._OrderRelationPlan = SqlInt32.Null;
			axisSupply._PlanUnitS = 0;
			axisSupply._StockDoc = stock;
			axisSupply._StockPlan = stock;
			axisSupply._StockUnit = StockUnit.Empty;      // Jedině po vložení Empty bude možno vložit nově určenou hodnotu metodou this.SetPlanUnitS(...)
			axisSupply._VariationInt = SqlInt32.Null;
			axisSupply._VariationPlan = SqlInt32.Null;
			axisSupply._VariationPus = SqlInt32.Null;
            axisSupply.DateOriginal = null;

			return axisSupply;
		}
		/// <summary>
		/// Vytvoří návrh na příjem na základě daného požadavku, množství, data příjmu a příznaku kumulace.
		/// Návrh na příjem se vytváří pro tu samou skladovou kartu, na které je požadvek. Nelze přeci příjem dát na jinou kartu, než kde je potřeba.
		/// Opačná situace je v metodě CreateRequest(), která zakládá návrh na výdej z dodavatelského skladu, více viz tam.
		/// </summary>
		/// <param name="stockUnit">Určení skladové karty</param>
		/// <param name="proposalQty">Množství návrhu</param>
		/// <param name="targetDate">Datum potřebného příjmu na skladovou kartu</param>
		/// <param name="isCumulated">Příznak, že požadované množství je kumulované z více různých požadavků. Pak axisRequest představuje první požadavek ze všech kumulovaných.</param>
		/// <returns>Data o návrhu na příjem</returns>
		internal static MaterialPlanAxisItemCls CreateProposalForMinQty(StockUnit stockUnit, decimal proposalQty, DateTime targetDate, bool isCumulated)
		{
			return null;
		}
		#endregion
		#region LOAD & SAVE Z/DO DATABÁZE
		/// <summary>
		/// Metoda vytvoří nový záznam osy S na základ dat z databáze
		/// </summary>
		/// <param name="record"></param>
		/// <returns></returns>
		internal static MaterialPlanAxisItemCls LoadFromDb(PlanUnitSAxisCls record)
		{
			MaterialPlanAxisItemCls axis = new MaterialPlanAxisItemCls();

			axis.RecordNumber = record.RecordNumber;               // číslo nonsubjektu uložím do dat v plánovacím procesu

			axis._DatePlanEnd = (record.DateAdj.IsNull ? record.Date : record.DateAdj.Value);
			axis._DatePlanBegin = record.DateStartPlan;
            axis._DateRealBegin = record.DateStartReal;
			axis._ChangeType = PlanningComponentCls.SqlInt16ToAxisChangeType(record.ChangeType);
			axis._OriginReason = PlanningComponentCls.SqlInt16ToAxisOriginReason(record.OriginReason);
			axis._StockUnit = new StockUnit(record.Stock, record.PlanUnitS);
			axis._AuthorGId = new GID(record.AuthorClass, record.AuthorSub);
			// record.Level = stockCard.Level;
			axis._PlanUnitS = record.PlanUnitS;
			// record.AConstrElement = SqlInt32.Null;
			axis._Qty = record.Qty + record.QtyPropAdj;
            axis._QtyPropWithLoss = record.QtyPropWithLoss;
			axis._QtyPropAdj = record.QtyPropAdj;
            axis._QtyPropWithLoss = record.QtyPropWithLoss;
			axis._QtyStD = record.QtyStD;
			axis._QtyD = record.QtyD;
			axis._QtyStDp = record.QtyStDp;
			axis._QtyDp = record.QtyDp;
			axis.ConcConf = record.ConcConf;
			// record.DeliveryTime = stockCard.StavSkDodavka;
			// record.DeliveryTimeRes = stockCard.StavSkRezerva;
			axis._AuthorObj = PlanningComponentCls.RelationToInt32(record.AuthorObj);
			axis._StockPlan = record.Stock;
			// record.Supplier = stockCard.AktualniDodavatel;
			axis._AuthorSubj = PlanningComponentCls.RelationToInt32(record.AuthorSub);
			axis._AuthorClass = PlanningComponentCls.RelationToInt32(record.AuthorClass);
			axis._ParentRecord = record.Parent;
			axis._AxisSeq = record.AxisSeq;
			axis.Transfer = record.Transfer;
			// record.Minimum = stockCard.MinStockQty;
			// record.UserInsert = Steward.CurrentUser.RecordNumber;
			// record.DateInsert = DateTime.Now;
			// record.Marker1 = axis.Marker1;
			// record.Marker2 = axis.Marker2;
			// record.Marker3 = axis.Marker3;
			// record.Marker4 = axis.Marker4;
			// record.Marker5 = axis.Marker5;
			// record.Marker6 = axis.Marker6;
			// record.Marker7 = axis.Marker7;
			// record.Marker8 = axis.Marker8;
			axis._AStructure = PlanningComponentCls.RelationToInt32(record.AStructure);
			// record.PlanUnitSSl = (stockCard.PlanUnitSSl == null ? SqlInt32.Null : new SqlInt32(stockCard.PlanUnitSSl.RecordNumber));
			// record.TypeRec = axis.TypeRec;
			axis._QtyStDpC = record.QtyStDpC;
			axis._QtyDpC = record.QtyDpC;
			axis._ProductOrderStructure = PlanningComponentCls.RelationToInt32(record.ProductOrderStructure);
			axis._AxisSeq2 = record.AxisSeq2;
			axis._AOperationMod = PlanningComponentCls.RelationToInt32(record.AOperationMod);
			axis._QtyStDp2 = record.QtyStDp2;
			axis._CatalogNumber = PlanningComponentCls.RelationToInt32(record.CatalogNumber);
			axis._ReferenceNumber = PlanningComponentCls.RelationToInt32(record.ReferenceNumber);
			axis._ReqCategories = PlanningComponentCls.RelationToInt32(record.ReqCategories);
			// axis._Action = record.Action;         // Nebudu načítat z DB: plánovací proces si vyhodnotí vlastní návrhy.
			axis._ConstrElement = PlanningComponentCls.RelationToInt32(record.ConstrElement);

			// Datum finálního výdeje, pokud má záznam charakter výdeje do poptávky:
			if (axis.ChangeType == PlanSAxisChangeType.DecrementByPlanEnquiry || axis.ChangeType == PlanSAxisChangeType.DecrementByRealEnquiry)
				axis._DateFinalRequest = record.Date;

            axis._DatePlanRequest = (record.DateFirstRequest.IsNull ? (DateTime?)null : record.DateFirstRequest.Value);
			return axis;
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitSAxisCls a jeho prostřednictvím do databáze.
		/// </summary>
		internal void SaveRecord(StockCardCls stockCard, PlanningParametersCls planningParameters)
		{
			// Tento záznam osy S se nemá plánovat:
			// if (!this.EnablePlanning) return;

			// Pokud není nalezena skladová karta, pak záznam osy S nebudu ukládat:
			if (stockCard == null) return;
			// Kdy to nastává: jde o kmenové karty, které se neplánují (proto jim není založena skladová karta).
			// Jak k tomu (ne)dojde? Při požadavku na skladovou kartu pro takovou KKZM není tato nalezena v MaterialData, 
			//   protože tam se načítají jen KKZM které se plánují.
			// Návazně ani tato karta neprochází MRP procesem, protože tím prochází jen položky, které jsou někde na skladu.

			// Najdeme záznam o pohybu na skladové kartě:
			StockChangeCls stockChange = (stockCard == null ? null : stockCard.FindStockChange(this.AxisID));
			if (stockChange == null) return;

			// Připravíme záznam osy S:
			using (PlanUnitSAxisCls record = new PlanUnitSAxisCls())
			{
				record.Date = this.DatePlanEnd;            // Pozor: pro výdej do poptávky bude upraveno na hodnotu this.DateFinalRequest!
				record.DateStartPlan = PlanningComponentCls.DateTimeToDateTimeNotNull(this.DatePlanBegin, this.DatePlanEnd);
                record.DateStartReal = this.DateRealBegin; // DAJ 2012-11-23 změnil na Real = pochází z první operace VP pro příjem z VP, jinak bude null    původně  = PlanningComponentCls.DateTimeToSqlDateTime(this.DatePlanBegin);
				record.Type = this.Type;
				record.Level = stockCard.Level;
				record.PlanUnitS = stockCard.PlanUnitSData.RecordNumber;       // Zde je uloženo platné číslo subjektu i pro PJ, které byly založeny v tomto běhu plánovacím procesu.
				record.AConstrElement = SqlInt32.Null;
				record.Qty = this.QtyRequest;
				record.QtyProp = this.QtyProp;
                record.QtyPropWithLoss = this.QtyPropWithLoss;
				record.QtyPropReal = this.QtyPropReal;
				record.QtyPropAdj = this.QtyPropAdj;
				record.QtyStD = this.QtyStD;
				record.QtyD = this.QtyD;
				record.QtyStDp = this.QtyStDp;
				record.QtyDp = this.QtyDp;
				record.ConcConf = this.ConcConf;
				record.DeliveryTime = stockCard.StavSkDodavka;
				record.DeliveryTimeRes = stockCard.StavSkRezerva;
				record.AuthorObj = PlanningComponentCls.Int32ToRelation(this.AuthorObj);
				record.Stock = this.StockPlan;
				record.Supplier = stockCard.AktualniDodavatel;
				record.AuthorSub = PlanningComponentCls.Int32ToRelation(this.AuthorSubj);
				record.Parent = this.ParentRecord;
				record.AxisSeq = this.AxisSeq;
				record.ExtInt = this.ExtInt;
				record.Origin = "G";
				record.Transfer = this.Transfer;
				record.Minimum = stockCard.MinStockQty;
				record.UserInsert = Steward.CurrentUser.RecordNumber;
				record.DateInsert = DateTime.Now;
				record.Marker1 = this.Marker1;
				record.Marker2 = this.Marker2;
				record.Marker3 = this.Marker3;
				record.Marker4 = this.Marker4;
				record.Marker5 = this.Marker5;
				record.Marker6 = this.Marker6;
				record.Marker7 = this.Marker7;
				record.Marker8 = this.Marker8;
				record.AStructure = this.AStructure;
				record.PlanUnitSSl = (stockCard.PlanUnitSSl == null ? SqlInt32.Null : new SqlInt32(stockCard.PlanUnitSSl.RecordNumber));
				record.TypeRec = this.TypeRec;
				record.QtyStDpC = this.QtyStDpC;
				record.QtyDpC = this.QtyDpC;
				record.ProductOrderStructure = PlanningComponentCls.Int32ToRelation(this.ProductOrderStructure);
				record.AxisSeq2 = this.AxisSeq2;
				record.AOperationMod = PlanningComponentCls.Int32ToRelation(this.AOperationMod);
				record.QtyStDp2 = this.QtyStDp2;
                //  plnění DateAdj bylo zavedeno DAJ 2012-11-05, a odebráno DAJ 2012-11-23.
                // record.DateAdj = PlanningComponentCls.DateTimeToSqlDateTime(this.DatePlanEnd);           // DAJ 2012-11-05
				record.CatalogNumber = PlanningComponentCls.Int32ToRelation(this.CatalogNumber);
				record.ReferenceNumber = PlanningComponentCls.Int32ToRelation(this.ReferenceNumber);
				record.ReqCategories = PlanningComponentCls.Int32ToRelation(this.ReqCategories);
				record.Action = this.Action;
				record.ConstrElement = PlanningComponentCls.Int32ToRelation(this.ConstrElement);
				record.PlanCVersion = planningParameters.PlanCVersion;
				record.ChangeType = PlanningComponentCls.AxisChangeTypeToSqlInt16(this.ChangeType);
				record.OriginReason = PlanningComponentCls.AxisOriginReasonToSqlInt16(this.OriginReason);
                record.DateFirstRequest = (this.DatePlanRequest.HasValue ? this.DatePlanRequest.Value : SqlDateTime.Null);

				// Datum finálního výdeje, pokud je zadané, a pokud má záznam charakter výdeje do poptávky (plán/reál), převedu do data na ose S:
				if (this.DateFinalRequest.HasValue && this.IsFromWhAnyEnquiry)
					record.Date = this.DateFinalRequest.Value;

				this.SaveViaRecord(record);
			}
		}
		/// <summary>
		/// Metoda pouze uloží hodnotu ParentRecord do své věty do databáze
		/// </summary>
		internal void SaveParentRecord()
		{
			if (this.RecordNumber <= 0)
				Throw.SysError(MessageInfo.Get("Není možno uložit hodnotu Parent do záznamu osy S, která dosud není uložena."));
			else
			{
				// Napřímo uložit jeden údaj do databáze :
				string sql = "UPDATE lcs.plan_unit_s_axis SET parent = " + SqlFormat.ToSql(this.ParentRecord) + " WHERE cislo_nonsubjektu = " + SqlFormat.ToSql(this.RecordNumber);
                Db_Layer.ExecuteNonQuery(sql);
			}
		}
        /// <summary>
        /// Změní charakter záznamu z Realita na Návrh, uloží změnu do databáze.
        /// Lze spustit pouze pro záznamy typu IncrementByRealProductOrder a DecrementByRealComponent, jinak vyhodí chybu.
        /// </summary>
        /// <param name="save"></param>
        internal void ChangeToProposal(bool save)
        {
            switch (this.ChangeType)
            {
                case PlanSAxisChangeType.IncrementByRealProductOrder:
                    this._ChangeType = PlanSAxisChangeType.IncrementByPlanProductOrder;
                    this._AuthorGId = GID.Empty;
                    this._AuthorClass = 0;
                    this._AuthorObj = 0;
                    this._AuthorSubj = 0;
                    if (save)
                        this._SaveChangeAuthor();
                    break;

                case PlanSAxisChangeType.DecrementByRealComponent:
                    this._ChangeType = PlanSAxisChangeType.DecrementByPlanComponent;
                    this._AuthorGId = GID.Empty;
                    this._AuthorClass = 0;
                    this._AuthorObj = 0;
                    this._AuthorSubj = 0;
                    if (save)
                        this._SaveChangeAuthor();
                    break;
                    
                default:
                    Throw.AplError(MessageInfo.Get("Metodu MaterialPlanAxisItemCls.ChangeToProposal() nelze použít na záznamu, jehož ChangeType == %0.", this.ChangeType));
                    break;
            }
        }
        /// <summary>
        /// Uloží do záznamu osy údaje:
        /// author_obj, author_sub, type_rec, change_type, action, qty, qty_prop, qty_prop_with_loss, qty_prop_real, qty_prop_adj
        /// </summary>
        private void _SaveChangeAuthor()
        {
            // Připravíme záznam osy S:
            using (PlanUnitSAxisCls record = new PlanUnitSAxisCls(this.RecordNumber))
            {
                record.AuthorObj = PlanningComponentCls.Int32ToRelation(this.AuthorObj);
                record.AuthorSub = PlanningComponentCls.Int32ToRelation(this.AuthorSubj);
                record.TypeRec = this.TypeRec;
                record.ChangeType = PlanningComponentCls.AxisChangeTypeToSqlInt16(this.ChangeType);
                record.Action = this.Action;                   // Vyhodnotí se doporučená akce (na základě dat v this.ChangeType) a převede se na char(1)
                record.Qty = this.QtyRequest;                  // Tato a následující property reagují na this.ChangeType, přiměřeně nastaví hodnotu do atributů na ose podle změněného záznamu (návrhu)
                record.QtyProp = this.QtyProp;
                record.QtyPropWithLoss = this.QtyPropWithLoss;
                record.QtyPropReal = this.QtyPropReal;
                record.QtyPropAdj = this.QtyPropAdj;

                record.UpdateColumns("author_obj, author_sub, type_rec, change_type, action, qty, qty_prop, qty_prop_with_loss, qty_prop_real, qty_prop_adj");
            }
        }
        #endregion
		#region PUBLIC STATIC METODY - KONVERTORY ENUMŮ, DETEKTORY


		#endregion
        #region Podpora pro auditování
        /// <summary>
        /// Zápis info do auditlogu
        /// </summary>
        /// <param name="info"></param>
        internal void Audit(string info)
        {
            if (!Steward.AppConfig.IsDebug(this.PlanUnitS, this.StockPlan)) return;

            string dateFmt = "dd.MM.yyyy HH:mm";
            string line = "AuditAxis " + info +
                "; Axis=" + this.AxisID.ToString() +
                "; PlanUnitS=" + this.PlanUnitS.ToString() +
                "; StockPlan=" + this.StockPlan.ToString() +
                "; DatePlanBegin=" + this.DatePlanBegin.ToString(dateFmt) +
                "; DatePlanEnd=" + this.DatePlanEnd.ToString(dateFmt) +
                "; Acquirement=" + this.Acquirement.ToString() +
                "; ChangeType=" + this.ChangeType.ToString() +
                "; ChangeQty=" + this.ChangeQty.ToString();
            Steward.Audit(AuditRowType.Info, line);
        }
        #endregion
    }
	#endregion
	#region CLASS CapacityPlanWorkItemCls : Jedna konkrétní výrobní operace, obsahuje jeden nebo více paralelních průchodů = kapacitní tasky.
	/// <summary>
	/// CapacityPlanWorkItemCls : Jedna konkrétní výrobní operace.
	/// Může se skládat z více paralelních průchodů.
	/// Teprve paralelní průchod je kapacitním úkolem
	/// </summary>
	public class CapacityPlanWorkItemCls : PlanItemCls<MaterialPlanAxisItemCls>, IOperationTime, IReferNazev
	{
		#region KONSTRUKCE - ZÁKLADNÍ PROMĚNNÉ
		/// <summary>
		/// Privátní konstruktor. Instanci této třídy je nutno vytvořit statickým konstruktorem s naplněním daty.
		/// </summary>
		private CapacityPlanWorkItemCls(MaterialPlanAxisItemCls parent) : base(parent)
		{
			this._SourceList = new List<ITaskSourceData>();
			this._WorkPassList = new List<WorkPassCls>();
		}
		#endregion
		#region STATICKÝ KONSTRUKTOR, VKLÁDÁNÍ ČASU, CLEAR
		/// <summary>
		/// Vytvoří záznam kapacitnho úkolu na základě dat o operaci, a přidaných parametrů.
		/// V záznamu založí prostor pro první paralelní průchod (WorkPassList[0]).
		/// Nevkládá datum zahájení alokace kapacit, výchozí fázi ani směr alokace.
		/// Opisuje příznak fixace tasku a čas fixace (TimeRange).
		/// </summary>
		/// <param name="parent">Reference na objekt Parent = záznam materiálové osy</param>
		/// <param name="tDocument">Objekt T dokumentace, k němuž patří daná operace</param>
		/// <param name="operationData">Data o operaci</param>
		/// <param name="planningParameters">Parametry procesu</param>
		/// <param name="limitedC">Typ omezení kapacit</param>
        /// <param name="parallelPasses">Předpřipravené rozdělení na paralelní průchody</param>
		/// <returns></returns>
        internal static CapacityPlanWorkItemCls CreateFromIOperation(MaterialPlanAxisItemCls parent, TDocumentCls tDocument, IOperationData operationData, MaterialDataCls materialData, PlanningParametersCls planningParameters, LimitedCType limitedC, IEnumerable<PlanningParallelPassCls> parallelPasses)
		{
			CapacityPlanWorkItemCls task = new CapacityPlanWorkItemCls(parent);

			// Přepíšu si informace o T dokumentu, abych mohl specifikovat dokument a dohledávat jeho další data a texty:
			task._DocumentType = tDocument.DocumentType;
			task._DocumentKey = tDocument.DocumentKey;
			task._OperationKey = operationData.Operation;
			task._OperationColor = operationData.OperationColor;

			// Uložit si do sebe definice operace z IOperationTime, 
			// z nich se bude vycházet při určování časů jednotlivých průchodů (this.WorkPassList):
			task.FillOperationTime(operationData);

			// Přepíšu data z IOperationData a z parametrů do tasku:
			task._CapacType = _ConvertTDocTypeToCapUsage(operationData.DocumentType);
			task._LimitType = limitedC;
            task._MfrOpPlanning = operationData.MfrOpPlanning;
			task._AllocMoveTimeMode = planningParameters.CapacityMoveTimeAllocMode;
			task._CalendarForMoveTime = planningParameters.CapacityMoveTimeCalendar;
			task._AllocNotPlanUnitMode = planningParameters.CapacityNoneUnitAllocMode;
			task._CalendarForNotPlanUnit = planningParameters.CapacityNoneUnitCalendar;
			task._ParalelPassingCount = operationData.ParalelPassingCount;
			task._UnitCount = operationData.UnitCount;
			task._MoveBatchQty = operationData.MoveBatchQty;
			task._QtyFinal = operationData.QtyFinal;
			task._QtyRequired = operationData.QtyRequired;
			task._IsFixedTask = operationData.IsFixed;
			task._FixedTime = (task._IsFixedTask ? operationData.FixedTime : TimeRange.Empty);

			// Naplnění soupisu kapacitních zdrojů:
			task.FillCSource(operationData);

            // Dohledání dělitelností pro dělení paralelních průchodů:
            task.FillDelitelnost(materialData);

            // Založím implicitní první průchod a naplním jej, anebo paralelní průchody podle předem připravených dat (parallelPasses):
            task.PrepareWorkPasses(parallelPasses);

			return task;
		}
        /// <summary>
		/// Do tohoto tasku vloží informace o časování operace (směr, výchozí fáze, výchozí datum).
		/// Pokud task obsahuje pouze jeden paralelní průchod na 100% požadovaného množství v tasku, propíše se i do tohoto jediného průchodu.
		/// Nutně musí dojít k vynulování (out) časů: TMcBeg, TMcEnd, ..., WorkBegin, WorkEnd a příznaků, viz metoda ClearResultData().
		/// </summary>
		/// <param name="timeDir">Směr alokace času</param>
		/// <param name="startPhase">Výchozí výrobní fáze, může být None = od první fáze v daném směru</param>
		/// <param name="startTime">Výchozí čas alokace tohoto kapacitního úkolu</param>
		internal void SetTiming(TimeRange.TimeDirection timeDir, OperationPhase startPhase, DateTime startTime)
		{
            this._SetTiming(timeDir, startPhase, startTime, LimitedCType.NotDefined, false);
		}
        /// <summary>
        /// Do tohoto tasku vloží informace o časování operace (směr, výchozí fáze, výchozí datum).
        /// Pokud task obsahuje pouze jeden paralelní průchod na 100% požadovaného množství v tasku, propíše se i do tohoto jediného průchodu.
        /// Nutně musí dojít k vynulování (out) časů: TMcBeg, TMcEnd, ..., WorkBegin, WorkEnd a příznaků, viz metoda ClearResultData().
        /// </summary>
        /// <param name="timeDir">Směr alokace času</param>
        /// <param name="startPhase">Výchozí výrobní fáze, může být None = od první fáze v daném směru</param>
        /// <param name="startTime">Výchozí čas alokace tohoto kapacitního úkolu</param>
        /// <param name="resetFixedTime">Resetovat FixedTime (ignorovat jej), používá se při ručním přeplánování operace</param>
        internal void SetTiming(TimeRange.TimeDirection timeDir, OperationPhase startPhase, DateTime startTime, bool resetFixedTime)
        {
            this._SetTiming(timeDir, startPhase, startTime, LimitedCType.NotDefined, resetFixedTime);
        }
        /// <summary>
		/// Do tohoto tasku vloží informace o časování operace (směr, výchozí fáze, výchozí datum).
		/// Pokud task obsahuje pouze jeden paralelní průchod na 100% požadovaného množství v tasku, propíše se i do tohoto jediného průchodu.
		/// Nutně musí dojít k vynulování (out) časů: TMcBeg, TMcEnd, ..., WorkBegin, WorkEnd a příznaků, viz metoda ClearResultData().
		/// </summary>
		/// <param name="timeDir">Směr alokace času</param>
		/// <param name="startPhase">Výchozí výrobní fáze, může být None = od první fáze v daném směru</param>
		/// <param name="startTime">Výchozí čas alokace tohoto kapacitního úkolu</param>
		/// <param name="limitedC">Typ omezení kapacit pro tento task</param>
		internal void SetTiming(TimeRange.TimeDirection timeDir, OperationPhase startPhase, DateTime startTime, LimitedCType limitedC)
		{
			this._SetTiming(timeDir, startPhase, startTime, limitedC, false);
		}
        /// <summary>
        /// Do tohoto tasku vloží informace o časování operace (směr, výchozí fáze, výchozí datum).
        /// Pokud task obsahuje pouze jeden paralelní průchod na 100% požadovaného množství v tasku, propíše se i do tohoto jediného průchodu.
        /// Nutně musí dojít k vynulování (out) časů: TMcBeg, TMcEnd, ..., WorkBegin, WorkEnd a příznaků, viz metoda ClearResultData().
        /// </summary>
        /// <param name="timeDir">Směr alokace času</param>
        /// <param name="startPhase">Výchozí výrobní fáze, může být None = od první fáze v daném směru</param>
        /// <param name="startTime">Výchozí čas alokace tohoto kapacitního úkolu</param>
        /// <param name="limitedC">Typ omezení kapacit pro tento task</param>
        /// <param name="resetFixedTime">Resetovat FixedTime (ignorovat jej), používá se při ručním přeplánování operace</param>
        internal void SetTiming(TimeRange.TimeDirection timeDir, OperationPhase startPhase, DateTime startTime, LimitedCType limitedC, bool resetFixedTime)
        {
            this._SetTiming(timeDir, startPhase, startTime, limitedC, resetFixedTime);
        }
        /// <summary>
		/// Do tohoto tasku vloží informace o časování operace (směr, výchozí fáze, výchozí datum).
		/// Pokud task obsahuje pouze jeden paralelní průchod na 100% požadovaného množství v tasku, propíše se i do tohoto jediného průchodu.
		/// Nutně musí dojít k vynulování (out) časů: TMcBeg, TMcEnd, ..., WorkBegin, WorkEnd a příznaků, viz metoda ClearResultData().
		/// </summary>
		/// <param name="timeDir">Směr alokace času</param>
		/// <param name="startPhase">Výchozí výrobní fáze, může být None = od první fáze v daném směru</param>
		/// <param name="startTime">Výchozí čas alokace tohoto kapacitního úkolu</param>
		/// <param name="limitedC">Typ omezení kapacit pro tento task</param>
        /// <param name="resetFixedTime">Resetovat FixedTime (ignorovat jej), používá se při ručním přeplánování operace</param>
        private void _SetTiming(TimeRange.TimeDirection timeDir, OperationPhase startPhase, DateTime startTime, LimitedCType limitedC, bool resetFixedTime)
		{
			if (limitedC != LimitedCType.NotDefined)
				this._LimitType = limitedC;
			this._TimeDir = timeDir;
			this._StartPhase = startPhase;
			this._StartTime = startTime;
            this._SetTimingToPass(resetFixedTime);
		}
		/// <summary>
		/// Do tohoto tasku vloží inforamaci o typu limitace kapacit.
		/// Pokud task obsahuje pouze jeden paralelní průchod na 100% požadovaného množství v tasku, propíše se i do tohoto jediného průchodu.
		/// Používá se pro explicitní nastavení v případě registrace kapacit k fixnímu termínu.
		/// </summary>
		/// <param name="limitedC">Typ omezení kapacit</param>
		internal void SetLimitType(LimitedCType limitedC)
		{
			this._LimitType = limitedC;
		}
		/// <summary>
		/// Pokud tento úkol obsahuje právě jeden task, jehož množství QtyRequired odpovídá množství this.QtyRequired, 
		/// pak do něj vepíšeme hodnoty this.TimeDir, this.StartPhase, this.StartTime.
		/// </summary>
        /// <param name="resetFixedTime">Resetovat FixedTime (ignorovat jej), používá se při ručním přeplánování operace</param>
        private void _SetTimingToPass(bool resetFixedTime)
		{
            this._ResetFixedTime = resetFixedTime;
            this._ReSetTiming();
        }
        /// <summary>
        /// Do všech paralelních průchodů vepíše znovu parametry časování, které do nich byly vepsány naposledy (metodou this.SetTiming()).
        /// Využívá se typicky po změně paralelních průchodů, před jejich registrováním do kapacit.
        /// </summary>
        internal void ReSetTiming()
        {
            this._ReSetTiming();
        }
        /// <summary>
        /// Metoda vrátí směr registrace konkrétní výrobní fázi.
        /// Metodu je možno použít poté, kdy do tasku byly vepsány časové údaje pro registraci kapacit metodou SetTiming().
        /// </summary>
        /// <param name="phase"></param>
        /// <returns></returns>
        internal TimeRange.TimeDirection GetTimeDirForPhase(OperationPhase phase)
        {
            return CapacityRegisterCls.GetTimeDirForPhase(phase, this._StartPhase, this._TimeDir);
        }
        /// <summary>
        /// Do všech paralelních průchodů vepíše znovu parametry časování, které do nich byly vepsány naposledy (metodou this.SetTiming()).
        /// Využívá se typicky po změně paralelních průchodů, před jejich registrováním do kapacit.
        /// </summary>
        private void _ReSetTiming()
        {
            foreach (WorkPassCls workPass in this.WorkPassList)
                workPass.SetTiming(this.TimeDir, this.StartPhase, this.StartTime, this._ResetFixedTime);
		}
        /// <summary>
        /// Pro všechny paralelní průchody zajistí smazání explicitního času fixovaného průchodu (a jeho odpovídajícího příznaku).
        /// Provádí se po registraci kapacit tohoto tasku, smysl má jen pro fixované tasky.
        /// Explicitní čas fixovaného průchodu se do WorkPass dostává z fixované operace VP, z podkladového paralelního průchodu (před novým během plánovacího procesu).
        /// </summary>
        internal void WorkPassesClearFixedTime()
        {
            foreach (WorkPassCls workPass in this.WorkPassList)
                workPass.ClearFixedTime();
        }
		/// <summary>
		/// Do tohoto kapacitního úkolu vepíše potřebné obecné kapacitní zdroje.
		/// </summary>
		/// <param name="operationData">Data o operaci</param>
		internal void FillCSource(IOperationData operationData)
		{
			if (this._SourceList == null) this._SourceList = new List<ITaskSourceData>();
			if (this._SourceList.Count > 0) this._SourceList.Clear();
			if (operationData == null) return;

			if (PlanningParametersCls.Current.CapacityRegisterForWorkplace)
			{
				// Požadované množství = podíl na kapacitě pracoviště: je odvozeno od počtu paralelních průchodů, pokud je menší než 1:
				decimal workplaceQty = 1M;
				if (operationData.ParalelPassingCount > 0M && operationData.ParalelPassingCount < 1M)
					workplaceQty = operationData.ParalelPassingCount;
				this._AddCSource(operationData.Workplace, CapacitySourceCategory.Workplace, workplaceQty, TResourceUnitQty.QtyToUnitCount);
			}
			if (PlanningParametersCls.Current.CapacityRegisterForProfession)
				this._AddCSource(operationData.Profession, CapacitySourceCategory.Profession, operationData.WorkersQty, TResourceUnitQty.QtyToUnitCount);

			if (PlanningParametersCls.Current.CapacityRegisterForResources)
			{
				foreach (IResourceData resource in operationData.ResourceList)
					this._AddCSource(resource.Source, CapacitySourceCategory.Resource, resource.Qty, resource.UnitQty);
			}
		}
		/// <summary>
		/// Do tohoto kapacitního úkolu vepíše časy operace
		/// </summary>
		/// <param name="operationData">Data o operaci</param>
		internal void FillOperationTime(IOperationData operationData)
		{
			this._MoveTime = operationData.MoveTime;
			this._MoveTimeMu = operationData.MoveTimeMu;
			this._Tbc = operationData.Tbc;
			this._TbcMu = operationData.TbcMu;
			this._Tac = operationData.Tac;
			this._TacMu = operationData.TacMu;
			this._TacDiv = operationData.TacDiv;
			this._Tec = operationData.Tec;
			this._TecMu = operationData.TecMu;
			this._ParalelPassingCount = operationData.ParalelPassingCount;
		}
		/// <summary>
		/// Do interního soupisu zdrojů vloží další kapacitní zdroj, pokud není null nebo nula
		/// </summary>
		/// <param name="capacitySource">CapacitySource = klíč zdroje kapacit, subjekt. Nejde o KPJ, ta se dohledává podle potřeby.</param>
		/// <param name="capacitySourceType">CapacitySourceType = Druh kapacity (pracoviště, profese, zdroj).</param>
		/// <param name="qty">Množství tohoto zdroje</param>
		/// <param name="unitQty">Násobnost množství</param>
		private void _AddCSource(SqlInt32 capacitySource, CapacitySourceCategory capacitySourceType, decimal qty, TResourceUnitQty unitQty)
		{
			if (capacitySource.IsNull) return;
			if (capacitySource.Value == 0) return;
			if (qty <= 0M) return;
			this._SourceList.Add(new TaskSourceData(capacitySource.Value, capacitySourceType, qty, unitQty));
		}
		/// <summary>
		/// Konverze typu dokumentu na typ čerpané kapacity (STPV = Plan, VP = Real)
		/// </summary>
		/// <param name="tDocumentType"></param>
		/// <returns></returns>
		private static CapacityUsageType _ConvertTDocTypeToCapUsage(TDocumentType tDocumentType)
		{
			switch (tDocumentType)
			{
				case TDocumentType.PlanApproved:
					return CapacityUsageType.Plan;
				case TDocumentType.Production:
					return CapacityUsageType.Real;
			}
			return CapacityUsageType.Calculation;
		}
		#endregion
		#region DATOVÉ PROMĚNNÉ
		// Dokument:
		private TDocumentType _DocumentType;
		private int _DocumentKey;
		private int _OperationKey;
		private Color _OperationColor = Color.Empty;

		// Task:
		private CapacityUsageType _CapacType;
		private bool _IsFixedTask;
		private TimeRange _FixedTime;
        private MfrOperationPlanning _MfrOpPlanning;
		private LimitedCType _LimitType;
		private AllocMode _AllocMoveTimeMode;
		private int _CalendarForMoveTime;
		private AllocMode _AllocNotPlanUnitMode;
		private int _CalendarForNotPlanUnit;
		private decimal _UnitCount;
		private decimal _MoveBatchQty;
		private decimal _QtyFinal;
		private decimal _QtyRequired;
		private DateTime _StartTime;
        private bool _ResetFixedTime;
		private OperationPhase _StartPhase;
		private TimeRange.TimeDirection _TimeDir;
		private List<ITaskSourceData> _SourceList;
		private List<WorkPassCls> _WorkPassList;
        private CapacityBatchTaskCls _CapacityBatchTask;

		// IOperationTime:
		private decimal _MoveTime;
		private int _MoveTimeMu;
		private decimal _Tbc;
		private int _TbcMu;
		private decimal _Tac;
		private int _TacMu;
		private bool _TacDiv;
		private decimal _Tec;
		private int _TecMu;
		private decimal _ParalelPassingCount;

		#endregion
		#region PUBLIC PROPERTY
		/// <summary> { get; } Klíč práce = můj primární klíč</summary>
		public int TaskID { get { return this.ItemID; } }
		/// <summary> { get; } Unikátní GID práce = číslo třídy plus můj primární klíč</summary>
		public GID TaskGID { get { return new GID(ClassNr, this.ItemID); } }
		/// <summary> { get; } DataPointer na tento task (GID řádku = osa S, AxisGID ; GID elementu = GID tohoto tasku)</summary>
		public DataPointerStr GraphPointerMainAxis { get { return new DataPointerStr(this.Parent.AxisGID, this.TaskGID); } }
		/// <summary> { get; } DataPointer na tento task (GID řádku = osa S, AxisGIDSubRow ; GID elementu = GID tohoto tasku)</summary>
		public DataPointerStr GraphPointerSubAxis { get { return new DataPointerStr(this.Parent.AxisGIDSubRow, this.TaskGID); } }
		/// <summary>Číslo třídy CapacityPlanWorkItemCls</summary>
		internal const int ClassNr = Constants.ClassNumberTask;
		/// <summary> { get; } Klíč záznamu na S ose (Parent.AxisID), pro který je tento úkol prováděn. Parent záznam (osa S) je příjmem z výroby.</summary>
		public int AxisID { get { return this.Parent.AxisID; } }
		/// <summary> { get; } Klíč záznamu na S ose (Parent.AxisGID), pro který je tento úkol prováděn. Parent záznam (osa S) je příjmem z výroby.</summary>
		public GID AxisGID { get { return this.Parent.AxisGID; } }
		/// <summary> { get; } Data osy S = materiálová osa</summary>
		internal MaterialPlanAxisItemCls AxisData { get { return this.Parent; } }
		/// <summary> { get; } Typ T dokumentu (VTPV / STPV / VP)</summary>
		public TDocumentType DocumentType { get { return this._DocumentType; } }
		/// <summary> { get; } Klíč T dokumentu (číslo subjektu dílce VTPV, T modifikace STPV, Výrobního příkazu), podle typu dokumentu</summary>
		public int DocumentKey { get { return this._DocumentKey; } }
		/// <summary> { get; } Klíč T operace (číslo nonsubjektu operace VTPV, STPV, Výrobního příkazu), podle typu dokumentu</summary>
		public int OperationKey { get { return this._OperationKey; } }
        /// <summary> { get; } Klíč GID T operace (obsahuje číslo třídy podle typu dokumentu a číslo záznamu this.OperationKey). Takový GID vytváří statická metoda CreateOperationGId().</summary>
        public GID OperationGId { get { return CreateOperationGId(this._DocumentType, this._OperationKey); } }
		/// <summary> { get; } Barva operace. Pokud je Empty, pak se barva přiřadí podle konfigurace pro typ položky (plán, výrobní příkaz s operací rozpracovanou / nerozpracovanou). Viz MfrPlanningConnectorCls._VisualGetColorForWorkUnit().</summary>
		public Color OperationColor { get { return this._OperationColor; } }
		/// <summary> { get; } Druh tasku, určuje cílový druh čerpaných kapacit (Reál, Plán, Simulace)</summary>
		public CapacityUsageType CapacType { get { return this._CapacType; } }
		/// <summary> { get; set; } Příznak, zda operace je časově fixovaná (true = fixovaná / false = volná)</summary>
		public bool IsFixedTask { get { return this._IsFixedTask; } internal set { this._IsFixedTask = value; } }
		/// <summary> { get; set; } Rozsah času, ve kterém je operace fixovaná (načítá se z tabulek, akceptuje se při plánování kapacit pro task)</summary>
		internal TimeRange FixedTime { get { return this._FixedTime; } set { this._FixedTime = value; } }
		/// <summary> { get; } Druh omezení kapacit, vychází z parametrů funkce Zaplánování kapacit. (Omezené, Neomezené, Podle KPJ)</summary>
		public LimitedCType LimitType { get { return this._LimitType; } }
        /// <summary>
        /// Plánovat tuto operaci, a jak?
        /// Tato hodnota je sem (do tasku) naplněna při tvorbě plánu z podkladové operace VP nebo STPV (prostřednictvím TOperation.MfrOpPlanning),
        /// a při načítání z databáze je tato hodnota dohledána v podkladové operaci VP nebo STPV (neukládá se do Tasku).
        /// </summary>
        public MfrOperationPlanning MfrOpPlanning
        {
            get { return this._MfrOpPlanning; }
            set { this._MfrOpPlanning = value; }
        }
        /// <summary>
        /// Vrací aktuální režim termínování oprace při jejím plánování.
        /// Vychází z režimu plánování (this.MfrOpPlanning).
        /// Pokud this.MfrOpPlanning == PlanByProcess (Plánovat = Ano), pak vrátí režim (operationTiming) zadaný na vstupu.
        /// Pokud this.MfrOpPlanning == NoPlanTime (Plánovat = Ne). pak vrátí režim StockLevelTime = čas operace = 0, akceptují se pouze časy Dodávka + Rezerva pro komponenty.
        /// Pokud this.MfrOpPlanning == PlanOnlyFlowTime (Plánovat = Pouze průběžnou dobu), pak vrátí režim LeadFeedTime = časy operace (TAC atd) se akceptují, ale plánují se dle kalendáře, a ne podle stavů kapacit.
        /// </summary>
        public MrpOperationTimingType GetCurrentTimingMode(MrpOperationTimingType operationTiming)
        {
            switch (this.MfrOpPlanning)
            {
                case MfrOperationPlanning.PlanByProcess: return operationTiming;
                case MfrOperationPlanning.NoPlanTime: return MrpOperationTimingType.StockLevelTime;
                case MfrOperationPlanning.PlanOnlyFlowTime: return MrpOperationTimingType.LeadFeedTime;
            }
            return operationTiming;
        }
		/// <summary> { get; } Způsob alokace času MoveTime. Defaultně nebývá nastavena hodnota "RegisterIntoUsedPlanUnits" (ale může být).
		/// Při zadání hodnoty AllocMoveTimeMode = CalculateTimeByCalendar se má zadat explicitní kalendář do CalendarForMoveTime.
		/// </summary>
		public AllocMode AllocMoveTimeMode { get { return this._AllocMoveTimeMode; } }
		/// <summary> { get; } Explicitní kalendář, použitý pro čas MoveTime při režimu : AllocMoveTimeMode = CalculateTimeByCalendar.</summary>
		public int CalendarForMoveTime { get { return this._CalendarForMoveTime; } }
		/// <summary> { get; } Způsob alokace času těch fází, kterých se neúčastní žádná KPJ (žádná nemá nastaveno Plánovat anebo Použít čas fáze).
		/// Při zadání hodnoty AllocNotPlanUnitMode = CalculateTimeByCalendar se má zadat explicitní kalendář do CalendarForNotPlanUnit.</summary>
		public AllocMode AllocNotPlanUnitMode { get { return this._AllocNotPlanUnitMode; } }
		/// <summary> { get; } Explicitní kalendář, použitý pro fáze bez KPJ, při režimu : AllocNotPlanUnitMode = CalculateTimeByCalendar.</summary>
		public int CalendarForNotPlanUnit { get { return this._CalendarForNotPlanUnit; } }
		/// <summary>Počet paralelních průchodů. Určuje strop paralelismu. Na širokokapacitních KPJ nelze použít víc průchodů, než je tento počet.</summary>
		public decimal ParalelPassingCount { get { return this._ParalelPassingCount; } }
		/// <summary> { get; } Počet najednou vyrobených kusů</summary>
		public decimal UnitCount { get { return this._UnitCount; } }
		/// <summary> { get; } Transportní dávka. Ovlivňuje výpočet času odkladu dávky i výpočet jednotkového času.</summary>
		public decimal MoveBatchQty { get { return this._MoveBatchQty; } }
		///<summary> { get; } Množství finálního výrobku celkem - toto množství se neplánuje, jen slouží k určování potřebnosti alokace fáze MoveTime, TBC, TEC.</summary>
		public decimal QtyFinal { get { return this._QtyFinal; } }
		///<summary> { get; } Množství chybějící, toto se bere do plánu. Fyzicky vyráběný počet na tomto tasku.</summary>
		public decimal QtyRequired { get { return this._QtyRequired; } }
        ///<summary> { get; } true, pokud QtyRequired je menší než QtyFinal (se zohledněním znamének)</summary>
        public bool IsPartial { get { return (Math.Abs(this._QtyRequired) < Math.Abs(this._QtyFinal)); } }
		/// <summary> { get; } Okamžik počátku registrace kapacit (výchozí FlowTime). Směr je dán v TimeDir.</summary>
		public DateTime StartTime { get { return this._StartTime; } }
		/// <summary> { get; } První fáze, která se bude alokovat. Umožní alokovat počínaje od fáze TAC (pokud plánujeme koordinaci výrobních dávek.</summary>
		public OperationPhase StartPhase { get { return this._StartPhase; } }
		/// <summary> { get; set; } 
		/// Směr alokace kapacit. Platí pro fázi StartPhase, a fáze po ní následující.
		/// Pokud alokace kapacit bude probíhat "odprostředka" (typicky od TAC), pak jde o směr dané fáze a fází po ní. Doplňkové fáze budou alokovány reverzně.
		/// Při směru do budoucna (ToFuture) se alokují kapacity počínaje od WorkBegin, a výsledný čas se ukládá do WorkEnd.
		/// Při směru do minulosti (ToHistory) se alokují kapacity počínaje od WorkEnd, a výsledný čas se ukládá do WorkBegin.
		/// </summary>
		public TimeRange.TimeDirection TimeDir { get { return this._TimeDir; } internal set { this._TimeDir = value; } }
		/// <summary> { get; set; } Soupis zdrojů kapacit, které se účastní práce. Konkrétní kapacitní plánovací jednotky budou dohledány později.</summary>
		public List<ITaskSourceData> SourceList { get { return this._SourceList; } set { this._SourceList = value; } }
		/// <summary> { get; } Soupis všech paralelních průchodů. Po inicializaci obsahuje první průchod, který je nastaven na 100% provedení úkolu.</summary>
		public List<WorkPassCls> WorkPassList { get { return this._WorkPassList; } }
        /// <summary>
        /// Kmenová karta na cílové ose S (=finál) má měrnou jednotku, která je dělitelná
        /// </summary>
        public bool AxisSourceIsDivisible { get; private set; }
        /// <summary> { get; set; } 
        /// Příznak, že paralelní průchody v tomto tasku byly interaktivně modifikovány. 
        /// Potom už jejich přeplánování nebude měnit jejich počet a množství (viz metoda PlanningDataCls._TaskRegisterPrepareWorkPass()).
        /// Nemá význam pro úkoly pocházející z Operace výrobního příkazu, tam se optimalizace neprovádí.
        /// Pokud se jedná o operaci STPV, bez této fixace WorkPassFixed, pak plánování kapacit může operaci (task) samo interaktivně rozdělit na paralelní průchody, 
        /// dané jednak technologicky (Operace STPV: Počet paralelních průchodů, paralel_passing_count) a jednak možnostmi kapacit (CapacityRegisterData.RegisterCapacityForTask(task, regArgs)).
        /// Optimalizaci průchodů pak řeší metoda PlanningDataCls.
        /// </summary>
        public bool WorkPassInteractiveChanged { get; set; }
        /// <summary>
        /// Vrací true tehdy, když paralelní průchody tohoto kapacitního úkolu je možno libovolně měnit (tím provádět automatické optimalizace).
        /// To je tehdy, když jde o operaci STPV (nebo VTPV) nebo Výrobního příkazu, která není fixovaná, a nebyla interaktivně dělena na paralelní průchody, a má povolen více než jeden paralelní průchod.
        /// Pokud je zde true, pak registrace kapacit sama rozdělí úkol na paralelní průchody podle dispozic STPV (Operace STPV: Počet paralelních průchodů, paralel_passing_count),
        /// a následně je optimalizuje podle dostupných kapacit.
        /// Pokud je zde false, pak se nic takového neděje a paralelní průchody se registrují tak, jak jsou zadány v tasku.
        /// </summary>
        public bool WorkPassOptimizationEnabled
        {
            get
            {
                return ((this.DocumentType == TDocumentType.PlanPrepared || this.DocumentType == TDocumentType.PlanApproved || this.DocumentType == TDocumentType.Production)
                    && !this.IsFixedTask && !this.WorkPassInteractiveChanged && this.ParalelPassingCount > 1m);
            }
        }
        /// <summary> { get; set; }
        /// Objekt obsahující výrobní dávky této operace.
        /// Je autoinicializační: pokud dosud nebyl použit, pak se před prvním čtením rekonstruuje z jednotlivých paralelních průchodů a jejich pracovních časů.
        /// Pokud tato operace nemá výrobní dávky, pak obsahuje objekt (not null), který je Empty (nemá konkrétní výrobní dávky).
        /// Objekt lze vložit. Pokud bude vloženo null (uloží se), pak příští čtení objektu provede rekalkulaci = dojde k invalidaci dat.
        /// Get ani Set neprovádějí kopii dat (klonování), to může provést ten, kdo s objektem pracuje.
        /// </summary>
        internal CapacityBatchTaskCls CapacityBatchTask
        {
            get 
            {
                if (this._CapacityBatchTask == null)
                    this.CapacityBatchRecalc();
                return this._CapacityBatchTask;
            }
            set
            {
                this._CapacityBatchTask = null;
            }
        }
		/// <summary>
		/// Časový interval celé operace = souhrn z jednotlivých paralelních průchodů.
		/// Pokud task neobsahuje žádné časy, vrací časový interval rovný času this.StartTime.
		/// </summary>
		public TimeRange TimeWork
		{
			get
			{
				TimeRange time = TimeRange.Empty;
				foreach (WorkPassCls pass in this.WorkPassList)
					time = time + pass.TimeWork;
				if (time.IsEmpty)
					time = new TimeRange(this.StartTime, this.StartTime);
				return time;
			}
		}
        /// <summary>
        /// Vrátí true, pokud celkový čas tohoto tasku (this.TimeWork) odpovídá danému vektoru timeVector:
        /// Pro směr ToFuture porovnává čas vektoru s časem počátku tasku (Begin),
        /// pro směr ToHistory porovnává čas vektoru s časem konce tasku (End).
        /// Vrací true, pokud je čas shodný / false pokud je jiný (neřeší větší / menší).
        /// </summary>
        /// <param name="timeVector"></param>
        /// <returns></returns>
        public bool TimeWorkMatchTimeVector(TimeVector timeVector)
        {
            TimeRange taskTime = this.TimeWork;            // Souhrnný čas tasku
            switch (timeVector.Direction)
            {
                case TimeRange.TimeDirection.ToFuture:
                    return (taskTime.Begin == timeVector.Date);
                case TimeRange.TimeDirection.ToHistory:
                    return (taskTime.End == timeVector.Date);
            }
            return false;
        }
        /// <summary>
		/// Časový interval fáze MoveTime této operace = souhrn časů MoveTime z jednotlivých paralelních průchodů
		/// </summary>
		public TimeRange TimeTMC
		{
			get
			{
				TimeRange time = TimeRange.Empty;
				foreach (WorkPassCls pass in this.WorkPassList)
					time = time + pass.TimeTMC;
				return time;
			}
		}
		/// <summary>
		/// Časový interval fáze TBC této operace = souhrn časů TBC z jednotlivých paralelních průchodů
		/// </summary>
		public TimeRange TimeTBC
		{
			get
			{
				TimeRange time = TimeRange.Empty;
				foreach (WorkPassCls pass in this.WorkPassList)
					time = time + pass.TimeTBC;
				return time;
			}
		}
		/// <summary>
		/// Časový interval fáze TAC této operace = souhrn časů TAC z jednotlivých paralelních průchodů
		/// </summary>
		public TimeRange TimeTAC
		{
			get
			{
				TimeRange time = TimeRange.Empty;
				foreach (WorkPassCls pass in this.WorkPassList)
					time = time + pass.TimeTAC;
				return time;
			}
		}
		/// <summary>
		/// Časový interval fáze TEC této operace = souhrn časů TEC z jednotlivých paralelních průchodů
		/// </summary>
		public TimeRange TimeTEC
		{
			get
			{
				TimeRange time = TimeRange.Empty;
				foreach (WorkPassCls pass in this.WorkPassList)
					time = time + pass.TimeTEC;
				return time;
			}
		}
		/// <summary>
		/// Celková potřebná kapacita pro tento task, vypočítává se jako součet potřebné kapacity jednotlivých fází.
		/// </summary>
		public decimal CapacityRequiredTotal
		{
			get
			{
				// Nejjednodušší cestou je vytvořit jeden průchod pro celý task, a sečíst jeho časy.
				// Vytvoření průchodu do něj vloží množství a vypočítá potřebné řasy TxcReq.
				// Objekt Pass vytvpřím jako Temporary (bez ID a bez vnitřních objektů):
				WorkPassCls pass = WorkPassCls.CreateTemporaryPass(this, this.QtyFinal, this.QtyRequired, this.UnitCount);
				return pass.TMcReq + pass.TBcReq + pass.TAcReq + pass.TEcReq;
			}
		}
        /// <summary>
        /// Reference: Název
        /// </summary>
        public string ReferNazev { get { return "#" + this.TaskID.ToString(); } }
		#endregion
        #region Podpora pro tvorbu paralelních průchodů
        /// <summary>
        /// Vytvoří takový počet paralelních průchodů pro tento task, jaký je uveden v this.ParalelPassingCount.
        /// Násobky množství jsou dány transportní dávkou (this.MoveBatchQty).
        /// Dělitelnost množství je detekována již v konstruktoru kapacitního úkolu, pro MJ podle KKZM z osy S (this.AxisSourceIsDivisible).
        /// Rozdíly ze zaokrouhlení řeší průběžně (plovoucí vyrovnání).
        /// Do posledního průchodu dá zůstatek, tím řeší rozdíly ze zaokrouhlení.
        /// </summary>
        internal void PrepareWorkPasses()
        {
            this.PrepareWorkPasses(false);
        }
        /// <summary>
        /// Vytvoří takový počet paralelních průchodů pro tento task, jaký je uveden v this.ParalelPassingCount.
        /// Násobky množství jsou dány transportní dávkou (this.MoveBatchQty).
        /// Dělitelnost množství je detekována již v konstruktoru kapacitního úkolu, pro MJ podle KKZM z osy S (this.AxisSourceIsDivisible).
        /// Rozdíly ze zaokrouhlení řeší průběžně (plovoucí vyrovnání).
        /// Do posledního průchodu dá zůstatek, tím řeší rozdíly ze zaokrouhlení.
        /// </summary>
        /// <param name="reSetTiming">Do založených průchodů vepsat jejich časování tak, jak bylo zachyceno v posledněm volání metody this.SetTiming()</param>
        internal void PrepareWorkPasses(bool reSetTiming)
        {
            int parallelPassCount = (int)(Math.Ceiling(this.ParalelPassingCount));
            if (parallelPassCount < 1) parallelPassCount = 1;
            this.PrepareWorkPasses(parallelPassCount, reSetTiming);
        }
        /// <summary>
        /// Vytvoří daný počet paralelních průchodů pro tento task.
        /// Respektuje daný zaokrouhlovací radix. Rozdíly ze zaokrouhlení řeší průběžně (plovoucí vyrovnání).
        /// Do posledního průchodu dá zůstatek, tím řeší rozdíly ze zaokrouhlení.
        /// </summary>
        /// <param name="parallelPassCount">Počet paralelních průchodů, Musí být kladné číslo.</param>
        /// <param name="delitel">Dělitel množství (=vytvoří se celé násobky), typicky this.MoveBatchQty</param>
        /// <param name="delitelnost">Je měrná jednotka dělitelná? Určuje zaokrouhlení na 0 nebo 6 míst</param>
        internal void PrepareWorkPasses(int parallelPassCount)
        {
            this.PrepareWorkPasses(parallelPassCount, false);
        }
        /// <summary>
        /// Vytvoří daný počet paralelních průchodů pro tento task.
        /// Respektuje daný zaokrouhlovací radix. Rozdíly ze zaokrouhlení řeší průběžně (plovoucí vyrovnání).
        /// Do posledního průchodu dá zůstatek, tím řeší rozdíly ze zaokrouhlení.
        /// </summary>
        /// <param name="parallelPassCount">Počet paralelních průchodů, Musí být kladné číslo.</param>
        /// <param name="reSetTiming">Do založených průchodů vepsat jejich časování tak, jak bylo zachyceno v posledněm volání metody this.SetTiming()</param>
        internal void PrepareWorkPasses(int parallelPassCount, bool reSetTiming)
        {
            if (parallelPassCount <= 0)
                Throw.AplError(MessageInfo.Get("Pro kapacitní úkol není možno vytvořit %0 paralelních průchodů, to je nemožný počet.", parallelPassCount));

            // Kolik je třeba vyrobit pro task, a kolik na jeden průchod:
            PlanningQtyCls taskQty = new PlanningQtyCls(this.QtyRequired, this.QtyFinal);   // Množství na task
            PlanningQtyCls passQty = taskQty / parallelPassCount;    // Ideální podíl na průchod, bez zaokrouhlení
            List<PlanningQtyCls> requestList = new List<PlanningQtyCls>();
            for (int p = 0; p < parallelPassCount; p++)              // Naplním soupis požadavků : počet prvků = parallelPassCount, každý prvek = passQty
                requestList.Add(passQty);

            // Zaokrouhlíme množství:
            List<PlanningQtyCls> qtyList = this.GetRoundWorkPassQtyList(requestList);

            // Vytvoříme průchody pro zaokrouhlené množství:
            this.PrepareWorkPasses(qtyList);

            // Set Timing:
            if (reSetTiming)
                this.ReSetTiming();
        }
        /// <summary>
        /// Metoda zajistí přepočet (zarovnání) hodnot v seznamu množství pro paralelní průchody.
        /// Na vstupu je soupis paralelních průchodů a množství "požadováno", na výstupu je totéž "zaokrouhleně" - v rámci this tasku.
        /// Výstup je new objekt List, obsahuje new instance (nikoli vstupní upravované).
        /// </summary>
        /// <param name="requestList"></param>
        /// <returns></returns>
        internal List<PlanningQtyCls> GetRoundWorkPassQtyList(IEnumerable<PlanningQtyCls> requests)
        {
            // Kolik je třeba vyrobit pro task, a kolik na jeden průchod:
            PlanningQtyCls taskQty = new PlanningQtyCls(this.QtyRequired, this.QtyFinal);   // Množství na task
            PlanningQtyCls sumaQty = new PlanningQtyCls();           // Průběžný součet požadovaných podílů, bez zaokrouhlování, teorie
            PlanningQtyCls roundQty = new PlanningQtyCls();          // Průběžný součet zaokrouhlených podílů, realita

            // Vytvořit podklady pro paralelní průchody:
            List<PlanningQtyCls> result = new List<PlanningQtyCls>();
            List<PlanningQtyCls> requestList = new List<PlanningQtyCls>(requests);
            int requestCount = requestList.Count;
            for (int p = 0; p < requestCount; p++)
            {
                PlanningQtyCls passQty = requestList[p];
                PlanningQtyCls realQty = null;
                if (p == (requestCount - 1))
                {   // Poslední průchod:
                    realQty = new PlanningQtyCls(taskQty);           // Do posledního průchodu dáme celé zbývající množství:
                }
                else
                {   // Ne-poslední průchody:
                    sumaQty.Add(passQty);                            // Kolik bychom chtěli mít vyrobeno po tomto paralelním průchodu bez ohledu na zaokrouhlení a zarovnání
                    realQty = sumaQty - roundQty;                    // Kolik v tomto průchodu vyrobíme, s připočtením zaokrouhlovacího rozdílu z minula, nezaokrouhleně
                    realQty = this.GetWorkPassRoundQty(realQty);     // Množství paralelního průchodu zarovnat na dělitele a zaokrouhlit podle dělitelnosti
                    roundQty.Add(realQty);                           // Kolik je po tomto paralelním průchodu vyrobeno reálně (se zaokrouhlením)
                }
                if (realQty.QtyRequired > 0m)
                    PrepareWorkPassesOne(realQty, taskQty, result);  // Do seznamu result přidat položku = Max(Min(realQty, taskQty), 0m), tuto položku odečíst od taskQty
            }
            if (taskQty.QtyRequired > 0m)                            // Pojistka poslední záchrany
                result.Add(taskQty);

            return result;
        }
        /// <summary>
        /// Vytvoří paralelní průchody z daného seznamu množství (PlanningQtyCls obsahuje Required i Final).
        /// </summary>
        /// <param name="qtyList"></param>
        internal void PrepareWorkPasses(IEnumerable<PlanningQtyCls> qtyList)
        {
            this.PrepareWorkPasses(qtyList, true);
        }
        /// <summary>
        /// Vytvoří paralelní průchody z daného seznamu množství (PlanningQtyCls obsahuje Required i Final).
        /// </summary>
        /// <param name="qtyList"></param>
        /// <param name="reSetTiming"></param>
        internal void PrepareWorkPasses(IEnumerable<PlanningQtyCls> qtyList, bool reSetTiming)
        {
            this.ClearWorkPasses();

            // Vytvořit předepsané průchody (bez zaokrouhlování, ale s hlídáním nepřekročení množství tasku):
            PlanningQtyCls taskQty = new PlanningQtyCls(this.QtyRequired, this.QtyFinal);   // Množství na task
            foreach (PlanningQtyCls passQty in qtyList)
            {
                PlanningQtyCls currQty = PrepareWorkPassesGetOne(passQty, taskQty);
                if (currQty != null)
                {
                    this.InsertNewWorkPass(currQty.QtyRequired, currQty.QtyFinal);
                    taskQty.Sub(currQty);
                }
            }

            // Pokud předepsané průchody nenaplnily množství tasku, dodat poslední průchod = zbytek tasku:
            if (taskQty.QtyRequired > 0m)
                this.InsertNewWorkPass(taskQty.QtyRequired, taskQty.QtyFinal);

            // Pokud task == 0m, pak nic nevzniklo - ale my potřebujeme alespoň jeden průchod s množstvím 0, kvůli registraci:
            if (this.WorkPassList.Count == 0)
                this.InsertNewWorkPass(0m, 0m);

            // Set Timing:
            if (reSetTiming)
                this.ReSetTiming();
        }
        /// <summary>
        /// Vytvoří paralelní průchody podle požadavků a zajistí implicitně potřebné množství.
        /// </summary>
        /// <param name="parallelPasses">Data o původních paralelních průchodech</param>
        /// <returns></returns>
        internal void PrepareWorkPasses(IEnumerable<PlanningParallelPassCls> parallelPasses)
        {
            this.PrepareWorkPasses(parallelPasses, false);
        }
        /// <summary>
        /// Vytvoří paralelní průchody podle požadavků a zajistí implicitně potřebné množství.
        /// </summary>
        /// <param name="parallelPasses">Data o původních paralelních průchodech</param>
        /// <param name="reSetTiming">Do založených průchodů vepsat jejich časování tak, jak bylo zachyceno v posledněm volání metody this.SetTiming()</param>
        /// <returns></returns>
        internal void PrepareWorkPasses(IEnumerable<PlanningParallelPassCls> parallelPasses, bool reSetTiming)
        {
            this.ClearWorkPasses();
            
            // Kolik je třeba vyrobit pro task:
            decimal taskQtyRequired = this.QtyRequired;
            decimal taskQtyFinal = this.QtyFinal;

            // Přidáme paralelní průchody dle dodaného soupisu, souběžně ale kontrolujeme množství qtyRequired a nepovolíme jeho přeplnění 
            //   (což může nastat po částečném odvedení operace, nebo po snížení množství na VP, když předešlý plán je na množství vyšší, než je nyní na operaci):
            if (parallelPasses != null)
                this.PrepareWorkPassesFrom(parallelPasses, ref taskQtyRequired, ref taskQtyFinal);

            // Pokud zůstalo v qtyRequired nějaké množství nezaplánované, doplníme další Pass na zbývající množství:
            //  a) to nastává běžně, když parallelPasses je null nebo empty
            //  b) anebo po navýšení množství na VP (anebo po stornu odvedení operace), kdy předešlé paralelní průchody jsou na množství původní = nižší)
            //  c) anebo když nevznikl žádný paralelní průchod (typicky: nefixovaná operace, množství na operaci == 0): pro takový task nutně musím založit nulový WorkPass, 
            //      jinak se neprovedou důležité metody registrace kapacit jako je plnění CapacityBatch registrů (ty se plní pro každý jednotlivý Pass):
            if (taskQtyRequired > 0m || this._WorkPassList.Count == 0)
                this.InsertNewWorkPass(taskQtyRequired, taskQtyFinal);

            // Set Timing:
            if (reSetTiming)
                this.ReSetTiming();
        }
        /// <summary>
        /// Metoda založí do tasku (this) paralelní průchody (WorkPass) na základě dat uložených v podkladových datech IEnumerable&lt;PlanningParallelPassCls&gt; parallelPasses.
        /// Ty pocházejí z dat o operaci výrobního příkazu.
        /// V této metodě se upravuje množství v průchodech tak, aby vyhovovalo požadovanému množství (qtyRequired), ve shodě se zadáním vývoje 0043680 "Částečné využ. kap. X Paralel. průchody".
        /// </summary>
        /// <param name="parallelPasses"></param>
        /// <param name="taskQtyRequired"></param>
        /// <param name="taskQtyFinal"></param>
        private void PrepareWorkPassesFrom(IEnumerable<PlanningParallelPassCls> parallelPasses, ref decimal taskQtyRequired, ref decimal taskQtyFinal)
        {
            List<PlanningParallelPassCls> parallelPassList = new List<PlanningParallelPassCls>(parallelPasses);
            int passCount = parallelPassList.Count;

            // Zkratka 0:
            if (passCount == 0)
                return;
            // Zkratka pro jeden paralelní průchod:
            if (passCount == 1)
            {   // Do paralelního průchodu uložíme množství celé potřebné, ignorujeme množství uložené v průchodu (důvodem změny množství na operaci je: změna množství VP, odvedení operace, storno odvedení):
                PlanningParallelPassCls parallelPass = parallelPassList[0];
                decimal qtyPassFinal = taskQtyFinal;
                decimal qtyPassRequired = taskQtyRequired;
                parallelPass.CreateWorkPassIntoTask(this, qtyPassFinal, qtyPassRequired, ref taskQtyFinal, ref taskQtyRequired);
                return;
            }

            // Máme více než 1 průchod: musíme vyřešit rozdělení množství aktuálního (z parametrů) do průchodů dle parallelPasses, se zachováním poměru jejich množství:

            // Nejprve si sečtu množství QtyRequired z podkladových průchodů (=ty paralelní průchody, které byly dosud navázané na operaci VP):
            decimal passQtyFinalSum = parallelPasses.Sum(pp => pp.QtyFinal);
            decimal passQtyRequiredSum = parallelPasses.Sum(pp => pp.QtyRequired);
            if (passQtyRequiredSum <= 0m)
                // Pokud původní průchody nic nemají, pak končím = nezaložím žádný průchod podle původních dat, nadřízená metoda založí implicitní jeden průchod na potřebné množství.
                return;

            decimal taskQtyFinalSum = taskQtyFinal;
            decimal taskQtyRequiredSum = taskQtyRequired;
            decimal ratioFinal = (passQtyFinalSum <= 0m ? 1m : taskQtyFinalSum / passQtyFinalSum);   // Poměrná změna množství Final
            decimal ratioRequired = taskQtyRequiredSum / passQtyRequiredSum;                         // Poměrná změna množství Required
            int roundRoot = PlanningComponentCls.GetRoundRoot(taskQtyRequiredSum, passQtyRequiredSum, taskQtyFinalSum, passQtyFinalSum);       // Kořen pro zaokrouhlování množství

            // Projdu připravená data o původních průchodech a založím průchody nové:
            for (int i = 0; i < passCount; i++)
            {
                // Pro poslední průchod platí: dáme do něj vše, co dosud zbývá (qtyFinal a qtyRequired), do předchozích průchodů dáme množství odpovídající koeficientu a zaokrouhlení:
                PlanningParallelPassCls parallelPass = parallelPassList[i];
                bool isLast = (i == (passCount - 1));
                decimal qtyPassFinal = (isLast ? taskQtyFinal : Math.Round(ratioFinal * parallelPass.QtyFinal, roundRoot));
                decimal qtyPassRequired = (isLast ? taskQtyRequired : Math.Round(ratioRequired * parallelPass.QtyRequired, roundRoot));
                parallelPass.CreateWorkPassIntoTask(this, qtyPassFinal, qtyPassRequired, ref taskQtyFinal, ref taskQtyRequired);
            }
        }
        /// <summary>
        /// Vytvoří nový paralelní průchod, pro dané množství.
        /// Zařadí si jej do svého seznamu WorkPassList.
        /// Vrátí jej, aby bylo možno s ním pracovat.
        /// </summary>
        /// <param name="qtyRequired"></param>
        /// <param name="qtyFinal"></param>
        /// <returns></returns>
        internal WorkPassCls InsertNewWorkPass(decimal qtyRequired, decimal qtyFinal)
        {
            WorkPassCls workPass = WorkPassCls.Create(this, qtyRequired, qtyFinal, this.UnitCount);
            this._WorkPassList.Add(workPass);
            return workPass;
        }
        /// <summary>
        /// Smaže seznam paralelních průchodů, ale jen ty, které nejsou registrovány do kapacit.
        /// </summary>
        private void ClearWorkPasses()
        {
            this._WorkPassList.RemoveAll(w => w.RegisterState == CapacityRegisterState.Initialized);
        }
        /// <summary>
        /// Metoda dohledá informace o výrobku z hlediska dělitelnosti a uloží je do this.AxisSourceIsDivisible.
        /// Metoda se volá v rámci konstruktoru.
        /// </summary>
        /// <param name="materialData"></param>
        private void FillDelitelnost(MaterialDataCls materialData)
        {
            this.AxisSourceIsDivisible = false;

            if (materialData != null)
            {
                KmenovaKartaSkladuCls kkzm = materialData.FindKmenovaKartaSkladu(this.AxisData.Source);     // Kmenová karta
                int mjKey = (!kkzm.TechnologickaMj.IsNull ? kkzm.TechnologickaMj.Value : (!kkzm.MernaJednotka.IsNull ? kkzm.MernaJednotka.Value : 0));
                if (mjKey != 0)
                {
                    MernaJednotkaCls mj = materialData.FindMernaJednotka(mjKey);
                    if (mj != null)
                        this.AxisSourceIsDivisible = mj.IsDivisible;
                }
            }
        }
        /// <summary>
        /// Do seznamu result přidá položku Max(Min(passQty, taskQty), 0m), tuto položku odečte z taskQty (změní hodnoty v taskQty).
        /// Pokud by položka byla 0m:0m, pak nedělá nic.
        /// </summary>
        /// <param name="passQty"></param>
        /// <param name="taskQty"></param>
        /// <param name="result"></param>
        private void PrepareWorkPassesOne(PlanningQtyCls passQty, PlanningQtyCls taskQty, List<PlanningQtyCls> result)
        {
            PlanningQtyCls qty = PrepareWorkPassesGetOne(passQty, taskQty);
            if (qty != null)
            {
                result.Add(qty);
                taskQty.Sub(qty);
            }
        }
        /// <summary>
        /// Vrátí Qty vytvořené jako Max(Min(passQty, taskQty), 0m).
        /// Data ve vstupních parametrech nemění, vrací new instanci (nebo null, pokud by instance obsahovala 0m : 0m).
        /// </summary>
        /// <param name="passQty"></param>
        /// <param name="taskQty"></param>
        /// <returns></returns>
        private PlanningQtyCls PrepareWorkPassesGetOne(PlanningQtyCls passQty, PlanningQtyCls taskQty)
        {
            decimal qtyRequired = (passQty.QtyRequired < 0m ? 0m : (passQty.QtyRequired < taskQty.QtyRequired ? passQty.QtyRequired : taskQty.QtyRequired));
            decimal qtyFinal = (passQty.QtyFinal < 0m ? 0m : (passQty.QtyFinal < taskQty.QtyFinal ? passQty.QtyFinal : taskQty.QtyFinal));
            if (qtyRequired <= 0m && qtyFinal <= 0m)
                return null;
            return new PlanningQtyCls(qtyRequired, qtyFinal);
        }
        /// <summary>
        ///  Metoda zarovná dané množství tak, aby odpovídalo pravidlům kmenové karty finálu (dělitelnost) a počtu v transportní dávce této operace (násobek).
        /// </summary>
        /// <param name="qty"></param>
        /// <returns></returns>
        internal PlanningQtyCls GetWorkPassRoundQty(PlanningQtyCls qty)
        {
            decimal qtyFinal = 0m;
            decimal qtyRequired = 0m;
            if (qty.QtyFinal != 0m)
            {
                qtyFinal = GetWorkPassRoundQty(qty.QtyFinal);

                if (qtyFinal == qty.QtyFinal)
                {
                    qtyRequired = qty.QtyRequired;
                }
                else
                {   // Pokud zarovnáním QtyFinal došlo ke změně jeho hodnoty, pak se asi změní i QtyRequired:
                    if (qty.IsSame)
                        qtyRequired = qtyFinal;
                    else
                    {
                        qtyRequired = qty.QtyRequired * qtyFinal / qty.QtyFinal;

                        bool delitelnost = this.AxisSourceIsDivisible;
                        if (delitelnost)
                            qtyRequired = Math.Round(qtyRequired, 6);
                        else
                            qtyRequired = Math.Round(qtyRequired, 0);
                        if (qtyRequired > qtyFinal)
                            qtyRequired = qtyFinal;

                    }
                }
            }
            return new PlanningQtyCls(qtyRequired, qtyFinal, qty.IsSame);
        }
        /// <summary>
        /// Metoda zarovná dané množství tak, aby odpovídalo pravidlům kmenové karty finálu (dělitelnost) a počtu v transportní dávce této operace (násobek).
        /// </summary>
        /// <param name="qty"></param>
        /// <returns></returns>
        internal decimal GetWorkPassRoundQty(decimal qty)
        {
            return GetWorkPassRoundQty(qty, this.MoveBatchQty, this.AxisSourceIsDivisible);
        }
        /// <summary>
        /// Vrátí dané množství (qty) zarovnané na dělitele (pokud delitel > 0), a zaokrouhlené na 0 nebo 6 míst, podle parametru delitelnost.
        /// </summary>
        /// <param name="qty"></param>
        /// <param name="delitel"></param>
        /// <param name="delitelnost"></param>
        /// <returns></returns>
        private static decimal GetWorkPassRoundQty(decimal qty, decimal delitel, bool delitelnost)
        {
            decimal result = qty;
            if (delitel > 0m)
                result = delitel * (Math.Ceiling(qty / delitel));    // Množství zarovnané nahoru na dělitele (například qty = 15, delitel = 7, result = 21)
            if (delitelnost)
                result = Math.Round(result, 6);
            else
                result = Math.Round(result, 0);
            return result;
        }
        #endregion
        #region IMPLEMENTACE IOperationTime
		// IOperationTime:
		/// <summary>Čas na přesun MOVE_TIME. Alokuje se před čas TBC.</summary>
		decimal IOperationTime.MoveTime { get { return this._MoveTime; } }
		/// <summary>Jednotka času zahájení výroby TBC (1=sekunda  60=minuta  3600=hodina).</summary>
		int IOperationTime.MoveTimeMu { get { return this._MoveTimeMu; } }
		/// <summary>Čas zahájení výroby TBC.</summary>
		decimal IOperationTime.Tbc { get { return this._Tbc; } }
		/// <summary>Jednotka času zahájení výroby TBC (1=sekunda  60=minuta  3600=hodina).</summary>
		int IOperationTime.TbcMu { get { return this._TbcMu; } }
		/// <summary>Čas výroby na jeden cyklus TAC. V jednom cyklu bude vyrobeno (UnitCount) jednotek.</summary>
		decimal IOperationTime.Tac { get { return this._Tac; } }
		/// <summary>Jednotka času výrobního cyklu TAC (1=sekunda  60=minuta  3600=hodina).</summary>
		int IOperationTime.TacMu { get { return this._TacMu; } }
		/// <summary>Příznak dělitelnosti množství na zlomky. Podle tohoto příznaku se bude zacházet s časy menšími, než TAcSec: pro nedělitelné operace bude čas kratší než TAcSec nepoužitelný (nevyrobím ani jeden kus). pro dělitelné operace jej využiju (vyrobím alespoň 0.028 litru).</summary>
		bool IOperationTime.TacDiv { get { return this._TacDiv; } }
		/// <summary>Čas ukončení výroby TEC.</summary>
		decimal IOperationTime.Tec { get { return this._Tec; } }
		/// <summary>Jednotka času ukončení výroby TEC (1=sekunda  60=minuta  3600=hodina).</summary>
		int IOperationTime.TecMu { get { return this._TecMu; } }
		/// <summary>Počet najednou vyráběných kusů. Jde o množství, vyrobené jedním chodem jednoho stroje. Při výrobě menšího množství je třeba zarovnat potřebný čas TA nahoru na (tac * UnitCount).</summary>
		decimal IOperationTime.UnitCount { get { return this._UnitCount; } }
		/// <summary>Počet paralelních průchodů. Určuje strop paralelismu. Na širokokapacitních KPJ nelze použít víc průchodů, než je tento počet.</summary>
		decimal IOperationTime.ParalelPassingCount { get { return this._ParalelPassingCount; } set { this._ParalelPassingCount = value; } }
		/// <summary>Transportní dávka. Ovlivňuje výpočet času odkladu dávky i výpočet jednotkového času.</summary>
		decimal IOperationTime.MoveBatchQty { get { return this._MoveBatchQty; } }
		#endregion
		#region LINK
		/// <summary>
		/// Číslo skupiny vztahů Link.
		/// Pokud je navázaný Link objektu (this.LinkObject není null), pak se číslo záznamu čte z jeho RecordNumber.
		/// Pokud Link objektu není navázaný, pak se číslo záznamu čte z privátní Int32 proměnné.
		/// Pokud není nastaveno nikde, vrací se 0 (ne Null).
		/// </summary>
		public int LinkGroup
		{
			get
			{
				if (this.LinkObject != null) return this.LinkObject.RecordNumber;
				if (!this.LinkRecordNumber.IsNull) return this.LinkRecordNumber.Value;
				return 0;
			}
		}
		/// <summary>
		/// Objekt, který představuje vztah mezi více záznamy tasků.
		/// Pokud je null, pak link představuje pouze hodnota v property this.LinkRecordNumber.
		/// Pokud není null, pak jeho RecordNumber představuje vztahovou hodnotu.
		/// Pokud není null, pak plánovací tabule zajišťuje jeho ukládání do databáze.
		/// Objekt lze setovat.
		/// </summary>
		public IRecord LinkObject
		{
			get { return this._LinkObject; } 
			set
			{
				this._LinkObject = value; 
				this._LinkRecordNumber = SqlInt32.Null;
			}
		}
		private IRecord _LinkObject;
		/// <summary>
		/// Číslo záznamu vztahového objektu.
		/// <para>
		/// Pokud je navázaný Link objektu (this.LinkObject není null), pak se číslo záznamu čte z jeho RecordNumber.
		/// V takovém případě není možno číslo záznamu zapisovat.
		/// </para>
		/// <para>
		/// Pokud Link objektu není navázaný, pak se číslo záznamu čte z privátní Int32 proměnné.
		/// V tom případě je možno číslo záznamu setovat.
		/// </para>
		/// </summary>
		public SqlInt32 LinkRecordNumber
		{
			get
			{
				if (_LinkObject != null)
					return this._LinkObject.RecordNumber;
				return this._LinkRecordNumber;
			}
			set
			{
				if (this._LinkObject != null)
					Throw.SysError(MessageInfo.Get("Nelze nastavit vztah na číslo Link záznamu, pokud je již navázaný Link objekt."));
				this._LinkRecordNumber = value;
			}
		}
		private SqlInt32 _LinkRecordNumber;
		/// <summary>
		/// Pokud je to vhodné, zajistí uložení objektu LinkObject do databáze.
		/// Poté je možno číst LinkRecordNumber, který bude obsahovat číslo záznamu z databáze.
		/// </summary>
		private void _LinkObjectSave()
		{
			if (this._LinkObject == null) return;
			this._LinkObject.Save();
		}
        /// <summary>
        /// Obsahuje (vždy vyhledá) klíč kapacitní plánovací jednotky pro tento task, která je primární.
        /// Primární je ta KPJ, která je nejvíce využívaná v roli Pracoviště (anebo Profese).
        /// </summary>
        public int PlanUnitCPrimary
        {
            get { return this._GetTaskPlanUnit(); }
        }
		#endregion
		#region ČTENÍ DAT O JEDNOTKÁCH PRÁCE A O VZTAZÍCH MEZI NIMI PRO JEJICH ZOBRAZOVÁNÍ
		/// <summary>
		/// Obsahuje true, pokud tento pracovní úkol obsahuje alespoň jednu položku pracovního průchodu s časem, obsahující nějaký WorkUnit.
		/// Jinými slovy, zda metoda this.CreateDataRelation() vygeneruje nějaká data.
		/// </summary>
		public bool ContainAnyWork
		{
			get
			{
				foreach (WorkPassCls workPass in this._WorkPassList)
				{
					if (workPass.ContainAnyWork) return true;
				}
				return false;
			}
		}
		/// <summary>
		/// Do předaného soupisu workUnitList přidá všechny své záznamy WorkUnit.
		/// </summary>
		/// <param name="workUnitList"></param>
		internal void CreateWorkUnitList(ref List<WorkUnitCls> workUnitList)
		{
			foreach (WorkPassCls workPass in this._WorkPassList)
				workPass.CreateWorkUnitList(ref workUnitList);
		}
		/// <summary>
		/// Vytvoří a vrátí seznam položek WorkUnit z tohoto tasku, kde položky jsou dané kategorie (pracoviště, profese...).
		/// Lze zadat více kategorií. 
		/// Pokud v první kategorii najdeme i jen jedinou položku, vrátí se seznam s touto jednou položkou.
		/// Pokud v první kategorii nenajdeme žádnou položku, budeme hledat položky v další kategorii.
		/// </summary>
		/// <param name="categories">Soupis hledaných kategorií.</param>
		/// <returns></returns>
		internal List<WorkUnitCls> GetWorkUnitListOfCategory(params CapacitySourceCategory[] categories)
		{
			List<WorkUnitCls> workUnitList = new List<WorkUnitCls>();
			if (categories != null)
			{
				foreach (CapacitySourceCategory category in categories)
				{
					foreach (WorkPassCls workPass in this._WorkPassList)
						workPass.GetWorkUnitListOfCategory(category, ref workUnitList);
					if (workUnitList.Count > 0) break;        // Pokud pro danou kategorii máme nějaký výsledek, skončím a vrátím ho.
					// Jinak jdu na další kategorii.
				}
			}
			return workUnitList;
		}
		/// <summary>
        /// Souhrn klíčů (ID): this + vnořených Pass + Time + Work
        /// </summary>
        internal string DebugKeys
        {
            get
            {
                string text = "Task " + this.TaskID.ToString() + "; Axis " + this.AxisID.ToString();
                if (this._WorkPassList.Count > 0)
                {
                    string prefix = "; PassList = {";
                    foreach (WorkPassCls workPass in this._WorkPassList)
                    {
                        text += prefix + workPass.DebugKeys;
                        prefix = ", ";
                    }
                    text += "}";
                }
                return text;
            }
        }
        /// <summary>
		/// Do předaného soupisu dataRelationList přidá záznamy o položkách a vztazích mezi položkami za tento task = za všechny jeho pracovní průchody,
		/// tedy za všechny jeho časy a jejich pracovní jednotky.
		/// </summary>
		/// <param name="dataRelationList">Ref výstup pro soupis položek Work a Relation.</param>
		internal void CreateDataRelation(ref List<DataRelation> dataRelationList)
		{
			foreach (WorkPassCls workPass in this._WorkPassList)
				workPass.CreateDataRelation(ref dataRelationList);
		}
		/// <summary>
		/// Metoda najde a vrátí klíč kapacitní plánovací jednotky, která je v dané roli nejvíce použita
		/// </summary>
		/// <param name="sourceCategory">Role (pracoviště, profese, přípravek)</param>
		/// <returns></returns>
		public int GetFirstPlanUnit(CapacitySourceCategory sourceCategory)
		{
			// Získám soupis všech KPJ (ze všech vnitřních položek práce), které jsou dané kategorie:
			Dictionary<int, decimal> usedDict = new Dictionary<int, decimal>();
			CreatePlanUnitList(sourceCategory, ref usedDict);

			// Z Dictionary udělám List, setřídím jej podle použité kapacity (pokud je to potřeba), a vrátím první nejpoužívanější KPJ:
			List<KeyValuePair<int, decimal>> usedList = new List<KeyValuePair<int, decimal>>(usedDict);
			if (usedList.Count > 0)
			{
				if (usedList.Count > 1)
					// Třídění podle použité kapacity, sestupně (a,b => b,a):
					usedList.Sort(delegate(KeyValuePair<int, decimal> a, KeyValuePair<int, decimal> b) { return Decimal.Compare(b.Value, a.Value); });

				return usedList[0].Key;
			}

			// Není použita žádná KPJ daného typu? Podívám se tedy do STPV, a najdu první zdroj daného typu, a najdu první KPJ pro něj:
			ITaskSourceData sourceData = this._SourceList.Find(source => source.CapacitySourceType == sourceCategory);
			if (sourceData == null) return 0;

			// Výstup: z nalezeného zdroje (dané kategorie) vrátím první KPJ, pokud jsou KPJ vyplněny v seznamu (což zajišťuje registrátor kapacit):
			return ((sourceData.CapacityUnitList != null && sourceData.CapacityUnitList.Count > 0) ? sourceData.CapacityUnitList[0].CapacityUnit : 0);
		}
		/// <summary>
		/// Metoda sestaví a vrátí seznam všech kapacitních jednotek, které jsou v tomto tasku nějak použity
		/// </summary>
		/// <returns></returns>
		public List<GID> GetAllPlanUnitCList()
		{
			Dictionary<int, decimal> planUnitCList = new Dictionary<int, decimal>();
			foreach (WorkPassCls workPass in this._WorkPassList)
				workPass.CreatePlanUnitList(CapacitySourceCategory.AllCategory, ref planUnitCList);

			List<GID> result = new List<GID>();
			foreach (int planUnitC in planUnitCList.Keys)
				result.Add(new GID(PlanUnitCCls.ClassNr, planUnitC));

			return result;
		}
		/// <summary>
		/// Metoda sestaví a vrátí seznam všech kapacitních jednotek, které jsou v tomto tasku nějak použity.
		/// Vrací soupis dvojic hodnot (KeyValuePair), kde Key = klíč kapacitní jednotky, a Value = použitá kapacita.
		/// </summary>
		/// <returns></returns>
		public List<KeyValuePair<int, decimal>> GetAllPlanUnitCCapacityList()
		{
			Dictionary<int, decimal> planUnitCList = new Dictionary<int, decimal>();
			foreach (WorkPassCls workPass in this._WorkPassList)
				workPass.CreatePlanUnitList(CapacitySourceCategory.AllCategory, ref planUnitCList);

			List<KeyValuePair<int, decimal>> result = new List<KeyValuePair<int, decimal>>(planUnitCList);
			return result;
		}
		/// <summary>
		/// Metoda vrátí seznam datapointerů na všechny WorkUnity (ze všech Pass - Time - Unit).
		/// V dataPointeru je řádek = kapacitní jednotka, a element = WorkUnit.
		/// </summary>
		/// <returns></returns>
		public void CreateAllWorkUnitPointers(ref List<DataPointerStr> pointerList)
		{
			foreach (WorkPassCls workPass in this._WorkPassList)
				workPass.CreateAllWorkUnitPointers(ref pointerList);
		}
		/// <summary>
		/// Metoda najde a do dodaného seznamu naplní použité kapacitní jednotky na dané kategorii, včetně jejich použité kapacity.
		/// Provede ze všech průchodů.
		/// </summary>
		/// <param name="sourceCategory">Požadovaný typ zdroje (např. pracoviště)</param>
		/// <param name="usedDict">Průběžně doplňovaný seznam KPJ a jejich časů</param>
		internal void CreatePlanUnitList(CapacitySourceCategory sourceCategory, ref Dictionary<int, decimal> usedDict)
		{
			foreach (WorkPassCls pass in this._WorkPassList)
				pass.CreatePlanUnitList(sourceCategory, ref usedDict);
		}
		/// <summary>
		/// Metoda sestaví a vrátí přehled časů jednotlivých výrobních fází.
		/// </summary>
		/// <returns></returns>
		public Dictionary<OperationPhase, TimeRange> GetPhaseTime()
		{
			Dictionary<OperationPhase, TimeRange> phaseDict = new Dictionary<OperationPhase, TimeRange>();
			phaseDict.Add(OperationPhase.MoveTime, TimeRange.Empty);
			phaseDict.Add(OperationPhase.TBC, TimeRange.Empty);
			phaseDict.Add(OperationPhase.TAC, TimeRange.Empty);
			phaseDict.Add(OperationPhase.TEC, TimeRange.Empty);
			this.CreatePhaseTime(ref phaseDict);
			this._CreatePhaseTimeSetAxisIfEmpty(ref phaseDict);
			return phaseDict;
		}
		/// <summary>
		/// Metoda zajistí naplnění seznamu fází jejich časy
		/// </summary>
		/// <param name="phaseDict"></param>
		internal void CreatePhaseTime(ref Dictionary<OperationPhase, TimeRange> phaseDict)
		{
			foreach (WorkPassCls pass in this._WorkPassList)
				pass.CreatePhaseTime(ref phaseDict);
		}
		/// <summary>
		/// Do soupisu časů vepíše datum z osy, pokud tento task je prázdný (tj. nejsou nalezeny žádné časy fází)
		/// </summary>
		/// <param name="phaseDict"></param>
		private void _CreatePhaseTimeSetAxisIfEmpty(ref Dictionary<OperationPhase, TimeRange> phaseDict)
		{
			TimeRange axisTime = this.Parent.DatePlanTime;
			this._CreatePhaseTimeSetAxisIfEmptyOne(OperationPhase.MoveTime, ref phaseDict, axisTime);
			this._CreatePhaseTimeSetAxisIfEmptyOne(OperationPhase.TBC, ref phaseDict, axisTime);
			this._CreatePhaseTimeSetAxisIfEmptyOne(OperationPhase.TAC, ref phaseDict, axisTime);
			this._CreatePhaseTimeSetAxisIfEmptyOne(OperationPhase.TEC, ref phaseDict, axisTime);
		}
		/// <summary>
		/// Do soupisu časů do konkrétní fáze vepíše datum z osy, pokud tento task je prázdný (tj. nejsou nalezeny žádné časy fází)
		/// </summary>
		/// <param name="phase"></param>
		/// <param name="phaseDict"></param>
		/// <param name="axisTime"></param>
		private void _CreatePhaseTimeSetAxisIfEmptyOne(OperationPhase phase, ref Dictionary<OperationPhase, TimeRange> phaseDict, TimeRange axisTime)
		{
			if (phaseDict.ContainsKey(phase) && phaseDict[phase].IsEmpty)
				phaseDict[phase] = axisTime;
		}
        /// <summary>
		/// Do předaného soupisu dataRelationList přidá záznamy o vzájemných vztazích 
		/// mezi všemi položkami WorkUnit dvou sousedních tasků (lastTask a currTask), pro všechny jejich WorkPass.
		/// Upozornění: 
		/// </summary>
        /// <param name="lastTask">Předchozí task</param>
        /// <param name="currTask">Aktuální task</param>
        /// <param name="timeDir">Směr času</param>
        /// <param name="relation"></param>
        /// <param name="dataRelationList">Ref výstup pro soupis položek Work a Relation.</param>
        internal static void CreateDataRelation(CapacityPlanWorkItemCls lastTask, CapacityPlanWorkItemCls currTask, TimeRange.TimeDirection timeDir, ElementRelationOrientation relation, ref List<DataRelation> dataRelationList)
        {
            CreateDataRelation(lastTask, 0, currTask, 0, timeDir, relation, ref dataRelationList);
        }
		/// <summary>
		/// Do předaného soupisu dataRelationList přidá záznamy o vzájemných vztazích 
		/// mezi všemi položkami WorkUnit dvou sousedních tasků (lastTask a currTask), pro všechny jejich WorkPass.
		/// Upozornění: 
		/// </summary>
		/// <param name="lastTask">Předchozí task</param>
        /// <param name="lastTaskOnlyPassId">ID paralelního průchodu z tasku lastTask, pro který (jediný z tasku) máme hledat vztahy, nebo 0 = pro všechny</param>
		/// <param name="currTask">Aktuální task</param>
        /// <param name="currTaskOnlyPassId">ID paralelního průchodu z tasku currTask, pro který (jediný z tasku) máme hledat vztahy, nebo 0 = pro všechny</param>
		/// <param name="timeDir">Směr času</param>
		/// <param name="relation"></param>
		/// <param name="dataRelationList">Ref výstup pro soupis položek Work a Relation.</param>
        internal static void CreateDataRelation(CapacityPlanWorkItemCls lastTask, int lastTaskOnlyPassId, CapacityPlanWorkItemCls currTask, int currTaskOnlyPassId, TimeRange.TimeDirection timeDir, ElementRelationOrientation relation, ref List<DataRelation> dataRelationList)
		{
			if (lastTask == null || currTask == null) return;
			if (!lastTask.ContainAnyWork || !currTask.ContainAnyWork) return;

			foreach (WorkPassCls lastPass in lastTask._WorkPassList)
			{
                // Pokud máme generovat jen za jeden paralelní průchod (lastTaskOnlyPassId), a ten aktuální (lastPass) to zrovna není, pak jej přeskočíme:
                if (lastTaskOnlyPassId != 0 && lastPass.PassID != lastTaskOnlyPassId) continue;

				WorkTimeCls lastTime = lastPass.GetOuterWorkTime(timeDir, false, true);
				foreach (WorkPassCls currPass in currTask._WorkPassList)
				{
                    if (currTaskOnlyPassId != 0 && currPass.PassID != currTaskOnlyPassId) continue;

                    WorkTimeCls currTime = currPass.GetOuterWorkTime(timeDir, true, true);
					switch (timeDir)
					{
						case TimeRange.TimeDirection.ToFuture:
							WorkTimeCls.CreateDataRelation(lastTime, currTime, relation, ref dataRelationList);
							break;
						case TimeRange.TimeDirection.ToHistory:
							WorkTimeCls.CreateDataRelation(currTime, lastTime, relation, ref dataRelationList);
							break;
					}
				}
			}
		}
        /// <summary>
        /// Tato metoda provede rekonstrukci časů jednotlivých výrobních dávek do svého objektu this.CapacityBatchTask
        /// na základě svých vnitřních výrobních časů WorkTime.
        /// Metodu lze zavolat kdykoliv, interně je volána při čtení hodnoty this.CapacityBatchTask (pokud by tam bylo null).
        /// Aby proběhl úspěšný přepočet, musí existovat alespoň jeden záznam WorkPassList a v něm čas TAC.
        /// </summary>
        /// <param name="capacityBatch"></param>
        internal void CapacityBatchRecalc()
        {
            CapacityBatchTaskCls batchTask = new CapacityBatchTaskCls();
            if (this._WorkPassList.Count == 0)
            {	// Není žádný paralelní průchod: nuluji výrobní dávky:
                batchTask.TaskRegisterNull();
            }
            else
            {   // Máme deklarované nějaké průchody => znovu sestavím z jednotlivých průchodů sadu výrobních dávek:
                batchTask.OneTaskInit();
                foreach (WorkPassCls workPass in this._WorkPassList)
                    workPass.CapacityBatchRecalc(batchTask);
                batchTask.OneTaskDone();
            }
            this._CapacityBatchTask = batchTask;
        }
        /// <summary>
        /// Příznak (true), zda tento task je dělitelný na transportní dávky
        /// </summary>
        internal bool IsTaskDivisibleToBatch
        {
            get
            {
                return CapacityBatchTaskCls.IsTaskDivisibleToBatch(this);
            }
        }
        /// <summary>
        /// Task si po změně vnitřních kapacit (Pass - Time - Unit) aktualizuje svoje hlavičkové údaje (časy, kapacitní jednotka, pracoviště)
        /// </summary>
        internal void RecalcTaskByInnerCapacity()
        {
            // Není co určovat:
            //  - Čas (WorkTime) se určuje dynamicky v get accessoru. 
            //  - PlanUnitCPrimary rovněž. 
            //  - Pracoviště (obecný kapacitní zdroj) na úrovni tohoto objektu neexistuje.
        }
		/// <summary>
		/// Pokusí se najít a vrátit průchod daného čísla ID / GID.
		/// Pokud nenajde, vrací null.
		/// </summary>
		/// <param name="passGID">Klíč hledaného průchodu</param>
		/// <returns></returns>
		internal WorkPassCls FindPassItem(GID passGID)
		{
			if (passGID.ClassNumber != Constants.ClassNumberPass) return null;
			int passID = passGID.RecordNumber;
			return this._WorkPassList.Find(delegate(WorkPassCls p) { return (p.PassID == passID); });
		}
		/// <summary>
		/// Pokusí se najít a vrátit průchod podle daného klíče WorkUnit.
		/// Pokud nenajde, vrací null.
		/// </summary>
		/// <param name="workGID">Klíč položky WorkUnit, jejíž Pass hledáme.</param>
		/// <returns></returns>
		internal WorkPassCls FindPassByWorkUnitGId(GID workGID)
		{
			if (workGID.ClassNumber != Constants.ClassNumberWork) return null;
			foreach (WorkPassCls workPass in this._WorkPassList)
			{
				if (workPass.ContainWorkUnit(workGID))
					return workPass;
			}
			return null;
		}
		/// <summary>
		/// Pokusí se najít a vrátit element práce (WorkUnit) nejvhodnější k dané původní lokaci.
		/// Tato metoda ověřuje shodu klíče tasku v lokátoru (TaskKey), pokud neodpovídá vrátí null.
		/// Pokud není možno nic najít, vrací null. K tomu může dojít jen tehdy, když:
		/// a) neexistuje ani jeden průchod (Pass)
		/// b) neexistuje žádná položka WorkTime pro danou fázi
		/// c) v žádné položce WorkTime (pro danou fázi) neexistuje ani jeden záznam WorkUnit.
		/// Jinak se snaží najít nejvhodnější (nejbližší) položku WorkTime a WorkUnit k danému lokátoru.
		/// </summary>
		/// <param name="location">Lokátor pozice. Popisuje fázi a relativní čas uvnitř fáze, popisuje typ kapacity (kategorii, zdroj, jednotku).</param>
		/// <returns></returns>
		internal WorkUnitCls FindUnitItem(WorkUnitLocationCls location)
		{
			return this._FindUnitItem(location, false, false);
		}
		/// <summary>
		/// Pokusí se najít a vrátit element práce (WorkUnit) nejvhodnější k dané původní lokaci.
		/// Tato metoda ověřuje shodu klíče tasku v lokátoru (TaskKey), pokud neodpovídá vrátí null.
		/// Pokud není možno nic najít, vrací null. K tomu může dojít jen tehdy, když:
		/// a) neexistuje ani jeden průchod (Pass)
		/// b) neexistuje žádná položka WorkTime pro danou fázi
		/// c) v žádné položce WorkTime (pro danou fázi) neexistuje ani jeden záznam WorkUnit.
		/// Jinak se snaží najít nejvhodnější (nejbližší) položku WorkTime a WorkUnit k danému lokátoru.
		/// </summary>
		/// <param name="location">Lokátor pozice. Popisuje fázi a relativní čas uvnitř fáze, popisuje typ kapacity (kategorii, zdroj, jednotku).</param>
		/// <param name="checkTaskKey">Ověřit číslo tasku. Pokud se nebude shodovat, vrátit null.</param>
		/// <returns></returns>
		internal WorkUnitCls FindUnitItem(WorkUnitLocationCls location, bool checkTaskKey)
		{
			return this._FindUnitItem(location, checkTaskKey, false);
		}
		/// <summary>
		/// Pokusí se najít a vrátit element práce (WorkUnit) nejvhodnější k dané původní lokaci.
		/// Tato metoda ověřuje shodu klíče tasku v lokátoru (TaskKey), pokud neodpovídá vrátí null.
		/// Pokud není možno nic najít, vrací null. K tomu může dojít jen tehdy, když:
		/// a) neexistuje ani jeden průchod (Pass)
		/// b) neexistuje žádná položka WorkTime pro danou fázi
		/// c) v žádné položce WorkTime (pro danou fázi) neexistuje ani jeden záznam WorkUnit.
		/// Jinak se snaží najít nejvhodnější (nejbližší) položku WorkTime a WorkUnit k danému lokátoru.
		/// </summary>
		/// <param name="location">Lokátor pozice. Popisuje fázi a relativní čas uvnitř fáze, popisuje typ kapacity (kategorii, zdroj, jednotku).</param>
		/// <param name="checkTaskKey">Ověřit číslo tasku. Pokud se nebude shodovat, vrátit null.</param>
		/// <param name="checkPassKey">Ověřit číslo paralelního průchodu. Pokud takový průchod neexistuje, vrátit null.</param>
		/// <returns></returns>
		internal WorkUnitCls FindUnitItem(WorkUnitLocationCls location, bool checkTaskKey, bool checkPassKey)
		{
			return this._FindUnitItem(location, checkTaskKey, checkPassKey);
		}
		/// <summary>
		/// Pokusí se najít a vrátit element práce (WorkUnit) nejvhodnější k dané původní lokaci.
		/// Tato metoda ověřuje shodu klíče tasku v lokátoru (TaskKey), pokud neodpovídá vrátí null.
		/// Pokud není možno nic najít, vrací null. K tomu může dojít jen tehdy, když:
		/// a) neexistuje ani jeden průchod (Pass)
		/// b) neexistuje žádná položka WorkTime pro danou fázi
		/// c) v žádné položce WorkTime (pro danou fázi) neexistuje ani jeden záznam WorkUnit.
		/// Jinak se snaží najít nejvhodnější (nejbližší) položku WorkTime a WorkUnit k danému lokátoru.
		/// </summary>
		/// <param name="location">Lokátor pozice. Popisuje fázi a relativní čas uvnitř fáze, popisuje typ kapacity (kategorii, zdroj, jednotku).</param>
		/// <param name="checkTaskKey">Ověřit číslo tasku. Pokud se nebude shodovat, vrátit null.</param>
		/// <param name="checkPassKey">Ověřit číslo paralelního průchodu. Pokud takový průchod neexistuje, vrátit null.</param>
		/// <returns></returns>
		private WorkUnitCls _FindUnitItem(WorkUnitLocationCls location, bool checkTaskKey, bool checkPassKey)
		{
			// Pokud se má kontrolovat shoda TaskID, zde je zadáno, a neshoduje se => skončit:
			if (checkTaskKey && location.TaskKey != 0 && location.TaskKey != this.TaskID)
				return null;          // Cizí lokátor: vrátit null.

			// Najít WorkPass, preferovat WorkPass s odpovídajícím klíčem:
			WorkUnitCls workUnitAny = null;
			foreach (WorkPassCls workPass in this._WorkPassList)
			{
				WorkUnitCls workUnit = workPass.FindUnitItem(location);
				if (workUnit != null)
				{
					if (workUnitAny == null)
						workUnitAny = workUnit; // První odpovídající WorkUnit.
					if (location.PassKey != 0 && workPass.PassID == location.PassKey)
						return workUnit;        // Našel jsem WorkUnit v explicitně daném WorkPass! Sláva, máme hotovo.
				}
			}
			// Pokud jsem zde, pak jsem nenašel WorkUnit v odpovídajícím WorkPass. (Protože když bych našel, tak bych dal return hnedka.)

			if (checkPassKey)
				return null;
			return workUnitAny;
		}
		/// <summary>
		/// Metoda najde a vrátí svůj OuterPointer.
		/// OuterPointer je adresa objektu WorkUnit, který je v daném směru vnějším v rámci této jednotky.
		/// Tato jednotka (Task) tedy najde krajní čas v daném směru (viz parametry) ze všech svých Pass, 
		/// a v něm najde nejvhodnější pracovní jednotku WorkUnit (první z existujících, v pořadí Workplace, Profession, Resource) a vrátí její pointer.
		/// Tento OuterPointer slouží k provazování sousedních operací a sousedních dílců.
		/// WorkPass není zodpovědný za hledání tohoto bodu v jiných WorkPass.
		/// Pokud tento Pass nemá žádný WorkUnit, vrací Empty.
		/// </summary>
		/// <param name="timeDir">Směr běhu času: 
		/// ToFuture při reverseDir = false vrací pointer na časově poslední záznam. 
		/// ToHistory při reverseDir = false vrací pointer na časově první záznam.
		/// </param>
		/// <param name="reverseDir">Obraceč času:
		/// ToFuture při reverseDir = true vrací pointer na časově první záznam. 
		/// ToHistory při reverseDir = true vrací pointer na časově poslední záznam.
		/// </param>
		/// <param name="withAnyWork">Požadavek, aby vrácený WorkTime obsahoval alespoň jednu položku WorkUnit (true) 
		/// / nebo false = vzít zcela krajní WorkTime bez ohledu na jeho WorkUnit</param>
		/// <returns>Pointer na nejvhodnější krajní element. Pokud tento Pass nemá žádný WorkUnit, vrací Empty.</returns>
		internal DataPointerStr GetOuterPointer(TimeRange.TimeDirection timeDir, bool reverseDir, bool withAnyWork)
		{
			DateTime outerTime;
			return GetOuterPointer(timeDir, reverseDir, withAnyWork, 0, out outerTime);
		}
        /// <summary>
        /// Metoda najde a vrátí svůj OuterPointer.
        /// OuterPointer je adresa objektu WorkUnit, který je v daném směru vnějším v rámci této jednotky.
        /// Tato jednotka (Task) tedy najde krajní čas v daném směru (viz parametry) ze všech svých Pass, 
        /// a v něm najde nejvhodnější pracovní jednotku WorkUnit (první z existujících, v pořadí Workplace, Profession, Resource) a vrátí její pointer.
        /// Tento OuterPointer slouží k provazování sousedních operací a sousedních dílců.
        /// WorkPass není zodpovědný za hledání tohoto bodu v jiných WorkPass.
        /// Pokud tento Pass nemá žádný WorkUnit, vrací Empty.
        /// </summary>
        /// <param name="timeDir">Směr běhu času: 
        /// ToFuture při reverseDir = false vrací pointer na časově poslední záznam. 
        /// ToHistory při reverseDir = false vrací pointer na časově první záznam.
        /// </param>
        /// <param name="reverseDir">Obraceč času:
        /// ToFuture při reverseDir = true vrací pointer na časově první záznam. 
        /// ToHistory při reverseDir = true vrací pointer na časově poslední záznam.
        /// </param>
        /// <param name="withAnyWork">Požadavek, aby vrácený WorkTime obsahoval alespoň jednu položku WorkUnit (true) 
        /// / nebo false = vzít zcela krajní WorkTime bez ohledu na jeho WorkUnit</param>
        /// <param name="onlyFromPassId">Pokud je nenulové, pak se má hledat pouze v tomto daném paralelním průchodu. 0 = hledá ve všech.</param>
        /// <returns>Pointer na nejvhodnější krajní element. Pokud tento Pass nemá žádný WorkUnit, vrací Empty.</returns>
        internal DataPointerStr GetOuterPointer(TimeRange.TimeDirection timeDir, bool reverseDir, bool withAnyWork, int onlyFromPassId)
        {
            DateTime outerTime;
            return GetOuterPointer(timeDir, reverseDir, withAnyWork, onlyFromPassId, out outerTime);
        }
        /// <summary>
		/// Metoda najde a vrátí svůj OuterPointer.
		/// OuterPointer je adresa objektu WorkUnit, který je v daném směru vnějším v rámci této jednotky.
		/// Tato jednotka (Task) tedy najde krajní čas v daném směru (viz parametry) ze všech svých Pass, 
		/// a v něm najde nejvhodnější pracovní jednotku WorkUnit (první z existujících, v pořadí Workplace, Profession, Resource) a vrátí její pointer.
		/// Tento OuterPointer slouží k provazování sousedních operací a sousedních dílců.
		/// WorkPass není zodpovědný za hledání tohoto bodu v jiných WorkPass.
		/// Pokud tento Pass nemá žádný WorkUnit, vrací Empty.
		/// </summary>
		/// <param name="timeDir">Směr běhu času: 
		/// ToFuture při reverseDir = false vrací pointer na časově poslední záznam. 
		/// ToHistory při reverseDir = false vrací pointer na časově první záznam.
		/// </param>
		/// <param name="reverseDir">Obraceč času:
		/// ToFuture při reverseDir = true vrací pointer na časově první záznam. 
		/// ToHistory při reverseDir = true vrací pointer na časově poslední záznam.
		/// </param>
		/// <param name="withAnyWork">Požadavek, aby vrácený WorkTime obsahoval alespoň jednu položku WorkUnit (true) 
		/// / nebo false = vzít zcela krajní WorkTime bez ohledu na jeho WorkUnit</param>
		/// <param name="outerTime">Out krajní čas, jen tak protože to jde</param>
		/// <returns>Pointer na nejvhodnější krajní element. Pokud tento Pass nemá žádný WorkUnit, vrací Empty.</returns>
		internal DataPointerStr GetOuterPointer(TimeRange.TimeDirection timeDir, bool reverseDir, bool withAnyWork, out DateTime outerTime)
		{
            return GetOuterPointer(timeDir, reverseDir, withAnyWork, 0, out outerTime);
        }
        /// <summary>
        /// Metoda najde a vrátí svůj OuterPointer.
        /// OuterPointer je adresa objektu WorkUnit, který je v daném směru vnějším v rámci této jednotky.
        /// Tato jednotka (Task) tedy najde krajní čas v daném směru (viz parametry) ze všech svých Pass, 
        /// a v něm najde nejvhodnější pracovní jednotku WorkUnit (první z existujících, v pořadí Workplace, Profession, Resource) a vrátí její pointer.
        /// Tento OuterPointer slouží k provazování sousedních operací a sousedních dílců.
        /// WorkPass není zodpovědný za hledání tohoto bodu v jiných WorkPass.
        /// Pokud tento Pass nemá žádný WorkUnit, vrací Empty.
        /// </summary>
        /// <param name="timeDir">Směr běhu času: 
        /// ToFuture při reverseDir = false vrací pointer na časově poslední záznam. 
        /// ToHistory při reverseDir = false vrací pointer na časově první záznam.
        /// </param>
        /// <param name="reverseDir">Obraceč času:
        /// ToFuture při reverseDir = true vrací pointer na časově první záznam. 
        /// ToHistory při reverseDir = true vrací pointer na časově poslední záznam.
        /// </param>
        /// <param name="withAnyWork">Požadavek, aby vrácený WorkTime obsahoval alespoň jednu položku WorkUnit (true) 
        /// / nebo false = vzít zcela krajní WorkTime bez ohledu na jeho WorkUnit</param>
        /// <param name="onlyFromPassId">Pokud je nenulové, pak se má hledat pouze v tomto daném paralelním průchodu. 0 = hledá ve všech.</param>
        /// <param name="outerTime">Out krajní čas, jen tak protože to jde</param>
        /// <returns>Pointer na nejvhodnější krajní element. Pokud tento Pass nemá žádný WorkUnit, vrací Empty.</returns>
        internal DataPointerStr GetOuterPointer(TimeRange.TimeDirection timeDir, bool reverseDir, bool withAnyWork, int onlyFromPassId, out DateTime outerTime)
        {
            outerTime = DateTime.MinValue;
			DataPointerStr result = DataPointerStr.Empty;

			// highest : true má vrátit pointer pro nejvyšší čas, false má vrátit pointer pro nejnižší čas:
			bool highest = ((timeDir == TimeRange.TimeDirection.ToFuture && !reverseDir) || (timeDir == TimeRange.TimeDirection.ToHistory && reverseDir));

			foreach (WorkPassCls workPass in this._WorkPassList)
			{
                // Hledat jen pro jeden Pass, a tenhle to není?  =>  Přeskočit:
                if (onlyFromPassId != 0 && workPass.PassID != onlyFromPassId)
                    continue;

				DateTime passOuterTime;
				DataPointerStr passPointer = workPass.GetOuterPointer(timeDir, reverseDir, withAnyWork, out passOuterTime);

				if (!passPointer.IsEmpty &&
					(result.IsEmpty || ((highest && passOuterTime > outerTime) || (!highest && passOuterTime < outerTime))))
				{
					result = passPointer;
					outerTime = passOuterTime;
				}
			}

			return result;
		}
		/// <summary>
		/// Vyhledá v paměti konkrétní časový úsek WorkTimeCls podle jeho GIDu.
		/// Tato metoda nemůže použít žádný index, hledá položku pomocí postupného scanování všech tasků -» pass -» time.
		/// Měla by být používána v minimální míře z důvodu časové náročnosti (systém na toto hledání není optimalizován).
		/// Metoda může vrátit null, pokud nic nenajde.
		/// </summary>
		/// <param name="timeGId"></param>
		/// <returns></returns>
		internal WorkTimeCls SearchForWorkTime(GID timeGId)
		{
			if (timeGId.ClassNumber != Constants.ClassNumberTime && timeGId.ClassNumber != PlanUnitCTimeCls.ClassNr) return null;
			foreach (WorkPassCls workPass in this._WorkPassList)
			{
				WorkTimeCls workTime = workPass.SearchForWorkTime(timeGId);
				if (workTime != null)
					return workTime;
			}
			return null;
		}
		#endregion
		#region PUBLIC PROPERTY PRO KONVERZI ZDEJŠÍCH DAT DO DAT GREEN
		///<summary><para>Atribut: Procento splnění [mfr_percent]</para><para>Db: lcs.plan_unit_c_task.perc_completed (decimal (5) not null)</para></summary>
		public decimal PercCompleted
		{
			get
			{
				decimal percRatio = 0M;
				if (this.DocumentType == TDocumentType.Production && this.QtyFinal > 0M)
				{
					percRatio = (this.QtyFinal - this.QtyRequired) / this.QtyFinal;
					percRatio = (percRatio < 0M ? 0M : (percRatio > 1M ? 1M : percRatio));
				}
				return 100M * percRatio;
			}
			set
			{
				decimal percRatio = value / 100M;
				if (this.DocumentType == TDocumentType.Production && this.QtyFinal > 0M && percRatio > 0M)
				{
					percRatio = (percRatio < 0M ? 0M : (percRatio > 1M ? 1M : percRatio));
					decimal qtyFinal = this.QtyRequired;
					if (percRatio < 1M)
						qtyFinal = this.QtyRequired / (1 - percRatio);
					this._QtyFinal = qtyFinal;
				}
			}
		}
		///<summary><para>Atribut: Kategorie [Ketegorie požadavku]</para><para>Db: lcs.plan_unit_c_task.category (char (1) not null)</para></summary>
		public string Category
		{ 
			get { return PlanningComponentCls.TDocTypeToMfrTaskCategory(this.DocumentType); }
		}
		///<summary><para>Atribut: Stavový příznak 1</para><para>Db: lcs.plan_unit_c_task.marker1 (char (1) not null)</para></summary>
		public string Marker1 { get { return _GetMarker(1); } }
		///<summary><para>Atribut: Stavový příznak 2</para><para>Db: lcs.plan_unit_c_task.marker2 (char (1) not null)</para></summary>
		public string Marker2 { get { return _GetMarker(2); } }
		///<summary><para>Atribut: Stavový příznak 3</para><para>Db: lcs.plan_unit_c_task.marker3 (char (1) not null)</para></summary>
		public string Marker3 { get { return _GetMarker(3); } }
		///<summary><para>Atribut: Stavový příznak 4</para><para>Db: lcs.plan_unit_c_task.marker4 (char (1) not null)</para></summary>
		public string Marker4 { get { return _GetMarker(4); } }
		///<summary><para>Atribut: Stavový příznak 5</para><para>Db: lcs.plan_unit_c_task.marker5 (char (1) not null)</para></summary>
		public string Marker5 { get { return _GetMarker(5); } }
		///<summary><para>Atribut: Stavový příznak 6</para><para>Db: lcs.plan_unit_c_task.marker6 (char (1) not null)</para></summary>
		public string Marker6 { get { return _GetMarker(6); } }
		///<summary><para>Atribut: Stavový příznak 7</para><para>Db: lcs.plan_unit_c_task.marker7 (char (1) not null)</para></summary>
		public string Marker7 { get { return _GetMarker(7); } }
		///<summary><para>Atribut: Stavový příznak 8</para><para>Db: lcs.plan_unit_c_task.marker8 (char (1) not null)</para></summary>
		public string Marker8 { get { return _GetMarker(8); } }
		/// <summary>
		/// Vrátí odpovídající marker
		/// </summary>
		/// <param name="markerNumber"></param>
		/// <returns></returns>
		private string _GetMarker(int markerNumber)
		{
			return "-";
		}
        /// <summary>
        /// Vrátí GID operace pro daný typ dokumentu a jeho číslo operace
        /// </summary>
        /// <param name="documentType"></param>
        /// <param name="operationKey"></param>
        /// <returns></returns>
        public static GID CreateOperationGId(TDocumentType documentType, int operationKey)
        {
            switch (documentType)
            {
                case TDocumentType.PlanPrepared:
                    return new GID(StandardMfrOperationsCls.ClassNr, operationKey);
                case TDocumentType.PlanApproved:
                    return new GID(AOperationCls.ClassNr, operationKey);
                case TDocumentType.Production:
                    return new GID(ProductOrderOperationCls.ClassNr, operationKey);
            }
            return GID.Empty;
        }
		#endregion
		#region LOAD & SAVE Z/DO DATABÁZE
		/// <summary>
		/// Vytvoří, naplní a vrátí záznam tasku na základě dat z databáze (record) plus parent záznam osy S, do něhož task míří.
		/// </summary>
		/// <param name="record">Data plan_unit_c_task</param>
		/// <param name="axisItem">Objekt plánovací osy S. Pozor, může být null.</param>
		/// <returns></returns>
		internal static CapacityPlanWorkItemCls LoadFromDb(PlanUnitCTaskCls record, MaterialPlanAxisItemCls axisItem)
		{
			if (axisItem == null) return null;

			CapacityPlanWorkItemCls task = new CapacityPlanWorkItemCls(axisItem);

			task.RecordNumber = record.RecordNumber;               // číslo nonsubjektu uložím do dat v plánovacím procesu

			task._QtyRequired = record.Qty;
			task._QtyFinal = record.Qty;                           // Lepší, než tam nechat 0
			task._Tbc = record.Tbc;
			task._TbcMu = record.TbcMu;
			task._Tac = record.Tac;
			task._TacMu = record.TacMu;
			task._Tec = record.Tec;
			task._TecMu = record.TecMu;
			task._MoveTime = record.MoveTime;
			task._ParalelPassingCount = (record.ParalelPassingCount.IsNull ? 1M : record.ParalelPassingCount.Value);
			task._UnitCount = record.UnitCount;
			task._StartTime = record.EndTask;
			task._TimeDir = TimeRange.TimeDirection.ToHistory;     // To proto, že jsem dal do task._StartTime čas EndTask.
			task._DocumentType = PlanningComponentCls.MfrTaskCategoryToTDocType(record.Category);
			switch (task._DocumentType)
			{
				case TDocumentType.PlanApproved:
                    if (axisItem.AOperationMod == 0 || record.AOperation.IsNull)
                        // Pokud na tasku není naplněna T modifikace, nebo operace STPV, pak skončím:
                        return null;
					task._DocumentKey = axisItem.AOperationMod;    // Klíč dokumentu = AOMod
					task._OperationKey = record.AOperation.Value;  // Klíč operace = STPV
					break;
				case TDocumentType.Production:
					if (axisItem.AuthorClass != ProductOrderCls.ClassNr || record.ProductOrderOperation.IsNull)
						// Pokud není na podkladové ose S navázaný jako author = výrobní příkaz,
						// anebo na tasku není vyplněna operace VP, pak skončím:
						return null;
					task._DocumentKey = axisItem.AuthorSubj;       // Klíč dokumentu = klíč VP
					task._OperationKey = record.ProductOrderOperation.Value;  // Operace VP
					if (record.PercCompleted > 0M)
						task.PercCompleted = record.PercCompleted; // set: dopočítá QtyFinal podle QtyRequired a (value)
					break;
                default:
                    // Jiné druhy záznamů nenačítám:
                    return null;
			}

			task._MoveTimeMu = record.MoveTimeMu;
			task._MoveBatchQty = record.MoveBatchQty;
			task._IsFixedTask = PlanningComponentCls.SqlInt16ToIsFixed(record.FixedOperation);
			task._LinkObject = null;
			task._LinkRecordNumber = record.Link;

			return task;
		}
		/// <summary>
		/// Metoda přidá další záznam o paralelním průchodu do tohoto tasku.
		/// Vrací PassID
		/// </summary>
		/// <param name="pass"></param>
		internal void DataLoadAddWorkPass(WorkPassCls pass)
		{
			this._WorkPassList.Add(pass);
		}
		/// <summary>
		/// Metoda zajistí finalizaci proměnných po načtení z databáze
		/// </summary>
		internal void DataLoadFinalize()
		{
			// Finalizovat načítání tasku:
			this.Parent.SetScheduledState(PlanSAxisSchedulingState.Scheduled);

			// Finalizovat jednotlivé průchody:
			foreach (WorkPassCls pass in this._WorkPassList)
				pass.DataLoadFinalize();
		}
		/// <summary>
		/// Metoda najde a vrátí nejmenší použitý poměr UseRatio z reálných položek.
		/// </summary>
		internal decimal DetectMinRealWorkplaceUseRatio()
		{
			decimal useRatio = 1M;
			foreach (WorkPassCls pass in this._WorkPassList)
			{
				foreach (WorkTimeCls time in pass.WorkTimeList)
				{
					foreach (WorkUnitCls unit in time.WorkUnitList)
					{
						if (unit.Category == CapacitySourceCategory.Workplace && unit.UseRatio < useRatio)
							useRatio = unit.UseRatio;
					}
				}
			}
			return useRatio;
		}
		/// <summary>
		/// Metoda vynuluje svoji hodnotu RecordNumber a totéž provede pro svoje přímé vnitřní členy.
		/// </summary>
		internal override void StoreEmptyRecordNumber()
		{
			base.StoreEmptyRecordNumber();
			foreach (WorkPassCls pass in this._WorkPassList)
				pass.StoreEmptyRecordNumber();
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitCTaskCls a jeho prostřednictvím do databáze.
		/// </summary>
		/// <param name="planningParameters">Parametry plánovacího procesu</param>
		internal void SaveRecord(PlanningParametersCls planningParameters)
		{
			this._SaveRecordTask(planningParameters);

			// this._SaveRecordScRel();

			// Uložíme i data vnitřních objektů (WorkPass, WorkTime, WorkUnit):
			foreach (WorkPassCls pass in this._WorkPassList)
				pass.SaveRecord();
		}
		/// <summary>
		/// Metoda uloží vlastní data Tasku, ale ne podřízené záznamy.
		/// </summary>
		/// <param name="planningParameters">Parametry plánovacího procesu</param>
		private void _SaveRecordTask(PlanningParametersCls planningParameters)
		{
			// Připravíme si soupis časů jednotlivých fází:
			Dictionary<OperationPhase, TimeRange> phaseTimeDict = this.GetPhaseTime();
			TimeRange timeWork = this.TimeWork;

			// Připravíme záznam tasku:
			using (PlanUnitCTaskCls record = new PlanUnitCTaskCls())
			{
				record.Qty = this.QtyRequired;
				record.Tbc = this._Tbc;
				record.TbcMu = this._TbcMu;
				record.Tac = this._Tac;
				record.TacMu = this._TacMu;
				record.Tec = this._Tec;
				record.TecMu = this._TecMu;
				record.MoveTime = this._MoveTime;
				record.ParalelPassingCount = this.ParalelPassingCount;
				record.UnitCount = this._UnitCount;
				record.StartTask = phaseTimeDict[OperationPhase.TBC].Begin;
				record.EndTask = timeWork.End;
				record.StartAdj = phaseTimeDict[OperationPhase.TBC].Begin;
				record.EndAdj = timeWork.End;
				record.NextTask = 0;
				record.PlanUnitC = this.PlanUnitCPrimary;
				record.PlanCVersion = planningParameters.PlanCVersion;
				record.PlanUnitSAxis = this.Parent.RecordNumber;
				record.ProductOrderOperation = (this.DocumentType == TDocumentType.Production ? new SqlInt32(this.OperationKey) : SqlInt32.Null);
				record.AOperation = (this.DocumentType == TDocumentType.PlanApproved ? new SqlInt32(this.OperationKey) : SqlInt32.Null);
				record.MoveTimeMu = this._MoveTimeMu;
				record.PercCompleted = this.PercCompleted;
				record.MoveBatchQty = this._MoveBatchQty;
				record.Category = this.Category;
				record.ReferenceNumber = this.Parent.ReferenceNumber;
				record.StartTac = phaseTimeDict[OperationPhase.TAC].Begin;
				record.StartTec = phaseTimeDict[OperationPhase.TEC].Begin;
				record.MoveTimeStart = phaseTimeDict[OperationPhase.MoveTime].Begin;
				record.CLlc = 0;
				record.DateCLlc = planningParameters.InitialDate;
				record.Marker1 = this.Marker1;
				record.Marker2 = this.Marker2;
				record.Marker3 = this.Marker3;
				record.Marker4 = this.Marker4;
				record.Marker5 = this.Marker5;
				record.Marker6 = this.Marker6;
				record.Marker7 = this.Marker7;
				record.Marker8 = this.Marker8;
				record.MoveTimeStartAdj = phaseTimeDict[OperationPhase.MoveTime].Begin;
				record.MfrFixedOperation = PlanningComponentCls.IsFixedToMfrFixed(this.IsFixedTask);   // bool to MfrFixedOperationType, a pak to set převede na SqlInt16
				this._LinkObjectSave();
				record.Link = this.LinkRecordNumber;

				this.SaveViaRecord(record);
			}
		}
		/// <summary>
		/// Metoda najde a vrátí klíč KPJ, která se vepisuje do tasku.
		/// Jde o nejpoužívanější KPJ pro kategorii pracoviště, nebo pokud se reálně nepracuje tak jde o první vhodné pracoviště.
		/// Pokud není pracoviště uvedeno, pak se hledá první KPJ pro první profesi.
		/// </summary>
		/// <returns></returns>
		private int _GetTaskPlanUnit()
		{
			int planUnitC = GetFirstPlanUnit(CapacitySourceCategory.Workplace);
			if (planUnitC != 0) return planUnitC;
			planUnitC = GetFirstPlanUnit(CapacitySourceCategory.Profession);
			if (planUnitC != 0) return planUnitC;
			return 0;
		}
        /// <summary>
        /// Změní charakter záznamu z Realita na Návrh, uloží změnu do databáze.
        /// </summary>
        /// <param name="save"></param>
        internal void ChangeToProposal(SqlInt32 aOperation, bool save)
        {
            switch (this.CapacType)
            {
                case CapacityUsageType.Real:
                    if (aOperation.IsNull)
                    {   // Není známa operace STPV:
                        this._DocumentType = TDocumentType.PlanPrepared;
                        this._CapacType = CapacityUsageType.Simulation;
                        this._OperationKey = 0;
                    }
                    else
                    {
                        this._DocumentType = TDocumentType.PlanApproved;
                        this._CapacType = CapacityUsageType.Plan;
                        this._OperationKey = aOperation.Value;
                    }
                    if (save)
                         this._SaveCapacType();
                    break;
                    
                default:
                    Throw.AplError(MessageInfo.Get("Metodu CapacityPlanWorkItemCls.ChangeToProposal() nelze použít na záznamu, jehož CapacType == %0.", this.CapacType));
                    break;
            }
        }
        /// <summary>
        /// Uloží do záznamu osy údaje:
        /// author_obj, author_sub, type_rec, change_type, action, qty, qty_prop, qty_prop_with_loss, qty_prop_real, qty_prop_adj
        /// </summary>
        private void _SaveCapacType()
        {
            // Připravíme záznam tasku:
            using (PlanUnitCTaskCls record = new PlanUnitCTaskCls(this.RecordNumber))
            {
                record.Category = this.Category;
                record.ProductOrderOperation = (this.DocumentType == TDocumentType.Production ? new SqlInt32(this.OperationKey) : SqlInt32.Null);
                record.AOperation = (this.DocumentType == TDocumentType.PlanApproved ? new SqlInt32(this.OperationKey) : SqlInt32.Null);

                record.UpdateColumns("category, product_order_operation, a_operation");
            }
        }
        #endregion
        #region DIAGNOSTIKA
        /// <summary>
        /// Přidá svoje data do diagnostiky (pár věcí z hlavičky, paralelní průchody, časy, jednotky)
        /// </summary>
        /// <param name="planningDataCls"></param>
        /// <param name="rowPrefix"></param>
        internal void AddToDiagnostic(PlanningDataCls planningDataCls, string title, string rowPrefix)
        {
            Steward.Diagnostic.SubTitleAdd(title);
            string pref = (rowPrefix == null ? "" : rowPrefix);
            Steward.Diagnostic.InfoAddLine(pref + "TASK " + planningDataCls.GetReferNazev(this.TaskGID) + "; QtyRequired=" + this.QtyRequired.ToString() + "; Time=" + this.TimeWork.ToString());
            for (int p = 0; p < this._WorkPassList.Count; p++)
            {
                WorkPassCls pass = this._WorkPassList[p];
                pass.AddToDiagnostic(planningDataCls, null, pref + " pass[" + p.ToString() + "]: ");
            }
        }
        #endregion
        #region Podpora pro auditování
        /// <summary>
        /// Zápis info do auditlogu
        /// </summary>
        /// <param name="info"></param>
        internal void Audit(string info)
        {
            MaterialPlanAxisItemCls axisItem = this.AxisData;
            if (!Steward.AppConfig.IsDebug(axisItem.PlanUnitS, axisItem.StockPlan)) return;

            TimeRange time = this.TimeWork;
            TimeRange fixedTime = this.FixedTime;
            string dateFmt = "dd.MM.yyyy HH:mm";
            string line = "AuditTask " + info +
                "; Task=" + this.TaskID.ToString() +
                "; Axis=" + this.AxisID.ToString() +
                "; TimeDir=" + this.TimeDir.ToString() +
                "; StartTime=" + this.StartTime.ToString() +
                "; StratPhase=" + this.StartPhase.ToString() +
                "; ResultTimeRange=" + time.StringShort +
                "; FixedTime=" + fixedTime.StringShort;
            Steward.Audit(AuditRowType.Info, line);
        }
        #endregion
    }
	#endregion
    #region INTERNÍ TŘÍDA TaskSourceData PRO UKLÁDÁNÍ POŽADOVANÝCH KAPACITNÍCH ZDROJŮ (ITaskSourceData)
    /// <summary>
    /// Jednoduchá třída, která implementuje ITaskSourceData
    /// </summary>
    internal class TaskSourceData : ITaskSourceData
    {
        private int _CapacitySource;
        private CapacitySourceCategory _CapacitySourceType;
        private decimal _Qty;
        private TResourceUnitQty _UnitQty;
        private bool _DisableUnitByTechnology;
        private bool _DetectByTechnology;
        private RegisterSuitabilityType _Suitability;
        private List<TaskSourceUnitData> _CapacityUnitList;
        private string _MissingCapacityInfo;

        /// <summary>
        /// Explicitní konstruktor
        /// </summary>
        /// <param name="capacitySource">CapacitySource = klíč zdroje kapacit, subjekt. Nejde o KPJ, ta se dohledává podle potřeby.</param>
        /// <param name="capacitySourceType">CapacitySourceType = Druh kapacity (pracoviště, profese, zdroj).</param>
        /// <param name="qty">Množství</param>
        /// <param name="unitQty">Jednotkové množství</param>
        public TaskSourceData(int capacitySource, CapacitySourceCategory capacitySourceType, decimal qty, TResourceUnitQty unitQty)
        {
            _CapacitySource = capacitySource;
            _CapacitySourceType = capacitySourceType;
            _Qty = qty;
            _UnitQty = unitQty;
            _Suitability = RegisterSuitabilityType.ByTechnology;
            _CapacityUnitList = new List<TaskSourceUnitData>();
            _MissingCapacityInfo = "";
        }
        /// <summary>
        /// Konstruktor vycházející z jiného objektu ITaskSourceData.
        /// Vytvoří nový objekt TaskSourceData, a naplní do něj data z podkladového objektu implementujícího ITaskSourceData, včetně klonování seznamu this.CapacityUnitList.
        /// </summary>
        /// <param name="originalData"></param>
        public TaskSourceData(ITaskSourceData originalData)
        {
            _CapacitySource = originalData.CapacitySource;
            _CapacitySourceType = originalData.CapacitySourceType;
            _Qty = originalData.Qty;
            _UnitQty = originalData.UnitQty;
            _Suitability = RegisterSuitabilityType.ByTechnology;
            _CapacityUnitList = new List<TaskSourceUnitData>();
            foreach (TaskSourceUnitData unit in originalData.CapacityUnitList)
                _CapacityUnitList.Add(new TaskSourceUnitData(unit));
            _MissingCapacityInfo = "";
        }
        #region ITaskSourceData Members
        /// <summary>
        /// Klíč zdroje kapacit. Obecná položka, kterou výroba požaduje.
        /// Ke zdroji kapacit se později najde vhodná plánovací jednotka.
        /// </summary>
        public int CapacitySource { get { return _CapacitySource; } }
        /// <summary>
        /// Druh kapacity (pracoviště, profese, zdroj).
        /// </summary>
        public CapacitySourceCategory CapacitySourceType { get { return _CapacitySourceType; } }
        /// <summary>
        /// Počet KPJ, potřebných na jeden průchod (Pass) nebo na jeden vyrobený kus (Product), více viz this.UnitQty
        /// </summary>
        public decimal Qty { get { return _Qty; } }
        /// <summary>Způsob určené potřebného množství zdroje s ohledem na UnitCount a Qty. 
        /// Pokud je UnitQty == TResourceUnitQty.QtyToUnitCount, pak na jeden průchod potřebujeme ITaskSourceData.Qty jednotek.
        /// Pokud je UnitQty == TResourceUnitQty.QtyToOneProduct, pak na jeden průchod potřebujeme ITaskSourceData.Qty * ITaskRegData.UnitCount jednotek.
        /// </summary>
        public TResourceUnitQty UnitQty { get { return _UnitQty; } }
        /// <summary>
        /// Majitel tasku zde může zakázat vyhledání KPJ pro Source podle standardních linků (ByTechnology).
        /// Typicky je zde false = registrátor si najde linky dle STPV.
        /// </summary>
        public bool DisableUnitByTechnology { get { return this._DisableUnitByTechnology; } set { this._DisableUnitByTechnology = value; } }
        /// <summary>
        /// Prostor pro registrátora kapacit, kam si ukládá informaci o tom, že již našel a doplnil KPJ dle technologie, pokud nejsou v DisableUnitByTechnology zakázány.
        /// Aplikace příznak nevyužívá.
        /// </summary>
        public bool DetectByTechnology { get { return this._DetectByTechnology; } set { this._DetectByTechnology = value; } }
        /// <summary>
        /// Režim, jakým způsobem se má zdroj kapacit plánovat.
        /// Default = ByTechnology.
        /// Řeší interaktivní přeplánování: různé kapacitní zdroje + jednotky se do kapacitního úkolu (WorkPassCls) přidávají při interaktivních změnách.
        /// Při tom se zakáže původní kapacitní zdroj (pokud se z něj odchází jinam) = dá se Excluded. Nově se naváže nový zdroj, dostane hodnotou Assigned = prioritní.
        /// </summary>
        public RegisterSuitabilityType Suitability { get { return this._Suitability; } set { this._Suitability = value; } }
        /// <summary>
        /// Prostor, kam registrátor ukládá informace o dostupných KPJ pro tento zdroj.
        /// Pokud majitel (zadavatel) tasku NECHCE sám diktovat kapacitní jednotky, sem připraví hodnotu null.
        /// Pak první proces registrace kapacit vyhledá vhodné jednotky pomocí Linků, a předplní je sem s typem Suitability = ByTechnology.
        /// Interaktivní procesy mohou měnit příznak Suitability u jednotlivých položek na Exclude nebo Assign.
        /// Majitel tasku může přikázat KPJ předem, pak je sem vloží s příznakem Assign.
        /// Majitel tasku může naplnit některé KPJ a v DisableUnitByTechnology nechat false, pak se do listu doplní i standardní technologické KPJ.
        /// Nebo může nastavit DisableUnitByTechnology = true, a pak se KPJ dle technologie nedohledávají.
        /// </summary>
        public List<TaskSourceUnitData> CapacityUnitList { get { return this._CapacityUnitList; } set { this._CapacityUnitList = value; } }
        /// <summary>
        /// Informace o chybějící kapacitě
        /// </summary>
        public string MissingCapacityInfo { get { return _MissingCapacityInfo; } set { _MissingCapacityInfo = value; } }
        #endregion
    }
    #endregion
    #region class PlanningItemRelationCls : Vazba mezi dvěma položkami plánu.
    /// <summary>
	/// PlanningItemRelationCls : Vazba mezi dvěma položkami plánu.
	/// Vazba je definované typem vztahu a ID obou položky (Prev, Next).
	/// </summary>
	public class PlanningItemRelationCls : PlanItemCls<object>         // Z předka používám AutoIncrement ItemID.
	{
		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="relationType">Typ vztahu</param>
		/// <param name="itemGIDPrev">GID prvku vlevo na časové ose, zdroj. 0 znamená odnikud (výchozí zásoba).</param>
		/// <param name="itemGIDNext">GID prvku vpravo na časové ose, cíl. 0 znamená nikam (koncová zásoba).</param>
		/// <param name="linkedQty">Předávané množství</param>
		internal PlanningItemRelationCls(PlanningItemRelationType relationType, GID itemGIDPrev, GID itemGIDNext, decimal linkedQty)
			: base(null)
		{
			this._Relation = relationType;
			this._ItemGIDPrev = itemGIDPrev;
			this._ItemGIDNext = itemGIDNext;
			this._LinkedQty = linkedQty;

			this._CheckEmpty();
		}
		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="relationType">Typ vztahu</param>
		/// <param name="timeDir">Směr času: ToFuture (prevID se předává do nextID), ToHistory (nextID se předává do prevID).</param>
		/// <param name="prevGID">GID prvku před, podle směru timeDir (ToFuture = vlevo, ToHistory = vpravo).</param>
		/// <param name="nextGID">GID prvku poté, podle směru timeDir (ToFuture = vpravo, ToHistory = vlevo).</param>
		/// <param name="linkedQty">Předávané množství</param>
		internal PlanningItemRelationCls(PlanningItemRelationType relationType, TimeRange.TimeDirection timeDir, GID prevGID, GID nextGID, decimal linkedQty)
			: base(null)
		{
			this._Relation = relationType;
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					this._ItemGIDPrev = prevGID;
					this._ItemGIDNext = nextGID;
					break;
				case TimeRange.TimeDirection.ToHistory:
					this._ItemGIDPrev = nextGID;
					this._ItemGIDNext = prevGID;
					break;
				default:
					Throw.SysError(MessageInfo.Get("Nelze využít časově reverzibilní tvorbu vztahu PlanningItemRelationCls pro směr času %0.", timeDir));
					break;
			}
			this._LinkedQty = linkedQty;

			this._CheckEmpty();
		}
		/// <summary>
		/// 
		/// </summary>
		private void _CheckEmpty()
		{
			if (this._ItemGIDPrev.IsEmpty && this._ItemGIDNext.IsEmpty)
			{}
		}
		/// <summary>
		/// ID tohoto záznamu vztahu
		/// </summary>
		internal int RelID { get { return this._ItemID; } }
		/// <summary>
		/// Skrývám property ItemID
		/// </summary>
		protected new int ItemID { get { return 0; } }
		/// <summary>
		/// Typ vztahu
		/// </summary>
		internal PlanningItemRelationType Relation { get { return this._Relation; } }
		private PlanningItemRelationType _Relation;
		/// <summary>
		/// GID položky předchozí.
		/// Jestli jde o GID Axis nebo Task lze určit podle čísla třídy anebo typu vztahu.
		/// </summary>
		internal GID ItemGIDPrev { get { return this._ItemGIDPrev; } }
		private GID _ItemGIDPrev;
		/// <summary>
		/// GID položky následující.
		/// Jestli jde o GID Axis nebo Task lze určit podle čísla třídy anebo typu vztahu.
		/// </summary>
		internal GID ItemGIDNext { get { return this._ItemGIDNext; } }
		private GID _ItemGIDNext;
		/// <summary>
		/// Množství, které se tímto vztahem převádí.
		/// </summary>
		internal decimal LinkedQty { get { return this._LinkedQty; } }
		private decimal _LinkedQty;
        /// <summary>
        /// Sada transportních dávek.
        /// Nachází se pouze na vztazích typu "Výdej do komponenty" (PlanningItemRelationType.AxisToTask).
        /// Obsahuje data získaná z objektu, který je umístěn na Kapacitním úkolu (CapacityPlanWorkItemCls.CapacityBatchTask).
        /// Jde o jiný typ, i jiné množství (na kapacitním úkolu se uvádí množství finálního výrobku, zde je množství konkrétní komponenty).
        /// </summary>
		internal TransferBatchSetCls TransferBatchSet { get { return this._TransferBatchSet; } set { this._TransferBatchSet = value; } }
		private TransferBatchSetCls _TransferBatchSet;
		/// <summary>
		/// Metoda vrátí ItemGIDPrev nebo ItemGIDNext, podle zadaného směru času a reverze.
		/// Pro ToFuture vrací ItemGIDNext, pro ToHistory vrací ItemGIDPrev.
		/// Pokud je zapnutá reverze (true), pak pro ToFuture vrací ItemGIDPrev, pro ToHistory vrací ItemGIDNext.
		/// </summary>
		/// <param name="timeDir">Směr času</param>
		/// <returns></returns>
		internal GID ItemGIDEdge(TimeRange.TimeDirection timeDir)
		{
			return this.ItemGIDEdge(timeDir, false);
		}
		/// <summary>
		/// Metoda vrátí ItemGIDPrev nebo ItemGIDNext, podle zadaného směru času a reverze.
		/// Pro ToFuture vrací ItemGIDNext, pro ToHistory vrací ItemGIDPrev.
		/// Pokud je zapnutá reverze (true), pak pro ToFuture vrací ItemGIDPrev, pro ToHistory vrací ItemGIDNext.
		/// </summary>
		/// <param name="timeDir">Směr času</param>
		/// <param name="reverseDir">Obraceč směru času, default = false</param>
		/// <returns></returns>
		internal GID ItemGIDEdge(TimeRange.TimeDirection timeDir, bool reverseDir)
		{
			TimeRange.TimeDirection dir = timeDir;
			if (reverseDir) dir = TimeRange.ReverseDirection(timeDir);
			switch (dir)
			{
				case TimeRange.TimeDirection.ToHistory:
					return this._ItemGIDPrev;
				case TimeRange.TimeDirection.ToFuture:
					return this._ItemGIDNext;
			}
			return GID.Empty;
		}
		/// <summary>
		/// true, pokud jsou obě strany vztahu prázdné (ItemGIDPrev i ItemGIDNext jsou Empty).
		/// </summary>
		internal bool IsEmpty { get { return (this._ItemGIDPrev.IsEmpty && this._ItemGIDNext.IsEmpty); } }
		/// <summary>
		/// Metoda vrátí true, pokud vztah v tomto záznamu je jeden z uvedených v parametrech
		/// </summary>
		/// <param name="relations">Soupis povolených vztahů</param>
		/// <returns></returns>
		internal bool IsRelationInList(params PlanningItemRelationType[] relations)
		{
			foreach (PlanningItemRelationType relType in relations)
			{
				if (this.Relation == relType) return true;
			}
			return false;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Relation: " + this.Relation + "; PrevID=" + this.ItemGIDPrev + "; NextID=" + this.ItemGIDNext + "; Qty=" + this.LinkedQty + "}";
		}
	}
	/// <summary>
	/// Typy vztahu mezi plánovacími položkami
	/// </summary>
	internal enum PlanningItemRelationType
	{
		/// <summary>
		/// Není vztah.
		/// </summary>
		NotRelation = 1,
		/// <summary>
		/// Primární vztah:
		/// Předání materiálu uvnitř jedné osy, klasické balancování (návrh na příjem - výdej).
		/// V tomto typu vztahu může být jedna strana vztahu prázdná (když je vstupem materiálu výchozí zásoba, anebo výstupem koncový zůstatek)
		/// </summary>
		AxisBalance,
		/// <summary>
		/// Primární vztah:
		/// Zásobení materiálem z jiného skladu, z jiné osy, ale stejná kmenová karta / dílec.
		/// Ani jedna strana nemůže být prázdná.
		/// </summary>
		AxisTransfer,
		/// <summary>
		/// Primární vztah:
		/// Výdej materiálu do výroby (komponenta)
		/// </summary>
		AxisToTask,
		/// <summary>
		/// Primární vztah:
		/// Příjem výrobku z výroby z poslední operace na osu S (výhradně příjem finálu, nikdy ne příjem vedlejšího produktu)
		/// </summary>
		TaskToAxisFinal,
		/// <summary>
		/// Primární vztah:
		/// Příjem vedlejšího produktu z výroby na osu S (nikdy ne příjem finálu, ten má vztah TaskToAxis).
		/// Pouze vedlejší produkt použitelný pro další výrobu.
		/// Pokud je vedlejší produkt nepoužitelný, je přijat na osu vztahem TaskToAxisDissonantProduct
		/// </summary>
		TaskToAxisByProductSuitable,
		/// <summary>
		/// Primární vztah:
		/// Příjem nepoužitelného (neshodného) vedlejšího produktu z výroby na osu S.
		/// Tento vedlejší produkt se nezapočítává do použitelné zásoby. Na záznamu osy S je uveden typ pohybu (ChangeType) Dissonant.
		/// (ToDo).
		/// </summary>
		TaskToAxisByProductDissonant,
		/// <summary>
		/// Primární vztah:
		/// Vztah mezi dvěma sousedními operacemi téhož dílce (uvnitř jedné výroby).
		/// </summary>
		TaskInner,
		/// <summary>
		/// Sekundární vztah:
		/// Vztah mezi operací koncovou jednoho dílce, a operací na vyšším dílci, do kterého tento finál vstupuje.
		/// Cílová operace nemusí být první operací nadřízeného dílce.
		/// </summary>
		TaskTransfer,
		/// <summary>
		/// Sekundární vztah:
		/// Vztah o výdeji ze záznamu osy S kamsi ven z výroby, typicky do obecné (ne konkrétní) poptávky anebo výroba na sklad.
		/// Cílová strana tohoto vztahu není GID konkrétní položky poptávky, ale GID typu Output (new GID(Constants.ClassNumberOutput, 0)).
		/// Důvod tohoto vztahu: jedním čtením ze systému vztahů najdeme všechny záznamy osy, které představují zobrazitelné položky.
		/// Hledá se tedy FindRelationPrevIDData(new GID(Constants.ClassNumberOutput, 0));
		/// Vrátí se vztahy, kde ItemPrev je číslo záznamu osy S, která je výdejkou z osy do poptávky,
		/// anebo je příjmem z výrobního příkazu, který není zcela spotřebován do poptávek.
		/// </summary>
		AxisOutput,
	}
	#endregion
	#region class PlanningTransferCls : Data o transferu množství a času
	/// <summary>
	/// PlanningRelationQtyCls : Data o transferu množství a času.
	/// Používá se při tvorbě mapy předávaného množství z jednoho záznamu (například příjem na osu z výrobního příkazu)
	///  přes skladové převody (balance a transfery) do výdeje do komponenty vyššího výrobního příkazu.
	/// Obsahuje množství, které se průtokem z výchozího do cílového záznamu zmenšuje,
	/// a obsahuje nastřádaný časový posun (kalendářní čas), vycházející z počtu dní na přesuny (dodávka + rezerva z jednotlivých záznamů na ose).
	/// Pracovní třída, není určena k trvalejšímu ukládání.
	/// </summary>
	internal class PlanningTransferCls
	{
		internal PlanningTransferCls(GID gid, TimeSpan delay, decimal qty)
		{
			this.Relation = null;
			this.GId = gid;
			this.Delay = delay;
			this.Qty = qty;
		}
		
		internal PlanningTransferCls(PlanningItemRelationCls relation, GID gid, TimeSpan delay, decimal qty)
		{
			this.Relation = relation;
			this.GId = gid;
			this.Delay = delay;
			this.Qty = qty;
		}
		public override string ToString()
		{
			return "GID: " + this.GId.ToString() + "; Delay: " + this.Delay.TotalDays.ToString("###.##") + " days; Qty=" + this.Qty.ToString();
		}
		/// <summary>
		/// Data vztahu, u převodů do tasku (AxisToTas) obsahují data transferu (property TransferBatchSetCls TransferBatchSet).
		/// </summary>
		internal PlanningItemRelationCls Relation { get; private set; }
		/// <summary>
		/// GID záznamu, do něhož dané množství přes transfer doputuje
		/// </summary>
		internal GID GId { get; private set ; }
		/// <summary>
		/// Zpoždění nastřádané za celý transfer až do tohoto bodu
		/// </summary>
		internal TimeSpan Delay { get; private set; }
		/// <summary>
		/// Množství, které se dostane do záznamu (GId) z výchozího množství
		/// </summary>
		internal decimal Qty { get; private set; }
		/// <summary>
		/// Transferová sada = informace o výdejích komponent do cílové operace, pokud je dělená na transportní dávky. Jinak je zde null.
		/// </summary>
		internal TransferBatchSetCls TransferSet { get { return (this.ContainTransferSet ? this.Relation.TransferBatchSet : null); } }
		/// <summary>
		/// Příznak, že tento objekt obsahuje transferovou sadu (v this.TransferSet).
		/// </summary>
		internal bool ContainTransferSet { get { return (this.Relation != null && this.Relation.TransferBatchSet != null); } }
		/// <summary>
		/// Počet jednotlivých položek v transferové sadě (this.TransferSet.Items). 
		/// Pokud tento objekt neobsahuje transferovou sadu, je zde -1.
		/// </summary>
		internal int TransferSetItemCount { get { return (this.ContainTransferSet ? this.Relation.TransferBatchSet.ItemCount : -1); } }
	}
	#endregion
	#region class PlanningQtyCls : jednoduchá třída, která uchovává množství QtyRequired a QtyFinal, slouží k přepočtům množství v tasku a průchodu
    /// <summary>
    /// PlanningQtyCls : jednoduchá třída, která uchovává množství QtyRequired a QtyFinal, slouží k přepočtům množství v tasku a průchodu.
    /// Implicitně zaokrouhluje na 6 míst.
    /// </summary>
    internal class PlanningQtyCls
    {
        internal PlanningQtyCls()
        {
            this._QtyRequired = 0m;
            this._QtyFinal = 0m;
            this.IsSame = true;
        }
        internal PlanningQtyCls(decimal qtyRequired)
        {
            this.QtyRequired = qtyRequired;
            this.QtyFinal = qtyRequired;
            this.IsSame = true;
        }
        internal PlanningQtyCls(decimal qtyRequired, decimal qtyFinal)
        {
            this.QtyRequired = qtyRequired;
            this.QtyFinal = qtyFinal;
            this.IsSame = (this.QtyRequired == this.QtyFinal);
        }
        internal PlanningQtyCls(decimal qtyRequired, decimal qtyFinal, bool isSame)
        {
            this.QtyRequired = qtyRequired;
            this.QtyFinal = qtyFinal;
            this.IsSame = isSame;
        }
        internal PlanningQtyCls(PlanningQtyCls source)
        {
            this.QtyRequired = source._QtyRequired;
            this.QtyFinal = source._QtyFinal;
            this.IsSame = source.IsSame;
        }
        public override string ToString()
        {
            return "Required: " + this.QtyRequired.ToString() + "; Final: " + this.QtyFinal.ToString();
        }
        /// <summary>
        /// Množství požadováno vyrobit. Může být shodné nebo menší než QtyFinal. Pro toto množství se plánují kapacity.
        /// </summary>
        internal decimal QtyRequired { get { return _QtyRequired; } set { _QtyRequired = Math.Round(value, 6); } }
        private decimal _QtyRequired;
        /// <summary>
        /// Množství požadováno celkem. Toto se nemusí nutně vyrábět, slouží k určení potřebnosti fáze MoveTime, TBC, TEC.
        /// </summary>
        internal decimal QtyFinal { get { return _QtyFinal; } set { _QtyFinal = Math.Round(value, 6); } }
        private decimal _QtyFinal;
        /// <summary>
        /// Příznak, že QtyFinal == QtyRequired
        /// </summary>
        internal bool IsSame { get; set; }
        #region Operátory, střadač
        /// <summary>
        /// Operátor "je rovno"
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(PlanningQtyCls a, PlanningQtyCls b)
        {
            bool aNull = ((object)a == null);
            bool bNull = ((object)b == null);
            if (aNull && bNull) return true;
            if (aNull || bNull) return false;
            return (a.QtyRequired == b.QtyRequired && a.QtyFinal == b.QtyFinal);
        }
        /// <summary>
        /// Operátor "není rovno"
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(PlanningQtyCls a, PlanningQtyCls b)
        {
            bool aNull = ((object)a == null);
            bool bNull = ((object)b == null);
            if (aNull && bNull) return false;
            if (aNull || bNull) return true;
            return (a.QtyRequired != b.QtyRequired || a.QtyFinal != b.QtyFinal);
        }
        /// <summary>
        /// Operátor "plus" = sčítání dvou hodnot
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static PlanningQtyCls operator +(PlanningQtyCls a, PlanningQtyCls b)
        {
            return new PlanningQtyCls(a.QtyRequired + b.QtyRequired, a.QtyFinal + b.QtyFinal, a.IsSame && b.IsSame);
        }
        /// <summary>
        /// Operátor "minus" = odečítání dvou hodnot
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static PlanningQtyCls operator -(PlanningQtyCls a, PlanningQtyCls b)
        {
            return new PlanningQtyCls(a.QtyRequired - b.QtyRequired, a.QtyFinal - b.QtyFinal, a.IsSame && b.IsSame);
        }
        /// <summary>
        /// Operátor "krát" = násobení
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static PlanningQtyCls operator *(PlanningQtyCls a, decimal ratio)
        {
            return new PlanningQtyCls(a.QtyRequired * ratio, a.QtyFinal * ratio, a.IsSame);
        }
        /// <summary>
        /// Operátor "lomeno" = dělení
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static PlanningQtyCls operator /(PlanningQtyCls a, decimal ratio)
        {
            return new PlanningQtyCls(a.QtyRequired / ratio, a.QtyFinal / ratio, a.IsSame);
        }
        /// <summary>
        /// Střadač +=
        /// </summary>
        /// <param name="other"></param>
        public void Add(PlanningQtyCls other)
        {
            this.QtyRequired += other.QtyRequired;
            this.QtyFinal += other.QtyFinal;
            this.IsSame = this.IsSame && other.IsSame;
        }
        /// <summary>
        /// Střadač -=
        /// </summary>
        /// <param name="other"></param>
        public void Sub(PlanningQtyCls other)
        {
            this.QtyRequired -= other.QtyRequired;
            this.QtyFinal -= other.QtyFinal;
            this.IsSame = this.IsSame && other.IsSame;
        }
        public PlanningQtyCls Negative { get { return new PlanningQtyCls(-this.QtyRequired, -this.QtyFinal, this.IsSame); } }
        #endregion
        /// <summary>
        /// Metoda zjistí, zda dva seznamy obsahují identické údaje v property QtyRequired.
        /// Seznamy musí mít shodný počet prvků, a údaje QtyRequired z jednoho seznamu musí korespondovat s druhým seznamem.
        /// Údaje nemusí být na shodné pozici, ale musí být k dohledání.
        /// Příklady: seznam {10,20,30} == {20,10,30}; {10,10,50} == {10,50,10};
        /// ale {10,10,20} != {10,20,20}; {10,20} != {10,20,20}.
        /// </summary>
        /// <param name="list1"></param>
        /// <param name="list2"></param>
        /// <returns></returns>
        internal static bool IsQtyRequiredInListsEqual(IEnumerable<PlanningQtyCls> list1, IEnumerable<PlanningQtyCls> list2)
        {
            Queue<PlanningQtyCls> queue = new Queue<PlanningQtyCls>(list1);   // Fronta: odsud odebírám jeden po druhém
            List<PlanningQtyCls> search = new List<PlanningQtyCls>(list2);    // Search: zde hledám a odebírám nalezené
            if (queue.Count != search.Count) return false;
            while (queue.Count > 0)
            {
                PlanningQtyCls item = queue.Dequeue();
                int index = search.FindIndex(q => q.QtyRequired == item.QtyRequired);
                if (index < 0) return false;
                search.RemoveAt(index);
            }
            return true;
        }

    }
	#endregion
}
namespace Noris.Schedule.Planning.DataFace
{
	#region EXTENDERY DATOVÝCH TŘÍD (DATAFACE), FORMOU partial class, UMOŽŇUJÍCÍ TVORBU NOVÝCH ZÁZNAMŮ NA ZÁKLADĚ EXPLICITNĚ PŘEDANÝCH ÚDAJŮ
	//  Extender třídy PlanUnitCTaskCls, přidává property MfrFixedOperation
	public partial class PlanUnitCTaskCls : RecordCls, Noris.Schedule.Support.Sql.IDbRecord
	{
		#region PŘIDANÉ PROPERTY
		/// <summary>
		/// Fixovaná operace, typová
		/// </summary>
		public MfrFixedOperationType MfrFixedOperation
		{
			get { return PlanningComponentCls.SqlInt16ToMfrFixedOperation(this.FixedOperation); }
			set { this.FixedOperation = PlanningComponentCls.MfrFixedOperationToSqlInt16(value); }
		}
		/// <summary> { get; }
		/// Je tato operace nějak fixovaná ? (FixedTime or FixedTimeAndUnit)
		/// </summary>
		public bool IsFixedOperation
		{
			get
			{
				MfrFixedOperationType mfrFixed = this.MfrFixedOperation;
				return (mfrFixed == MfrFixedOperationType.Fixed);
			}
		}
		#endregion
	}
	#endregion
}